01 Documentation

Languages:

Introduction

Welcome! FaunaDB is a distributed, multi-tenant, multi-model database system with a powerful query language. This developer guide will introduce the key concepts of FaunaDB.

Getting Started

We provide some tutorials to help you get started quickly. We recommend you to take a look at them in order to get familiar with the basic concepts of FaunaDB.

Drivers

FaunaDB uses the HTTP protocol, making it easy to use with most modern programming languages.

We officially support drivers in the following languages:

Protocol

FaunaDB uses HTTP as its primary transport. Unless otherwise specified, request bodies are expected to be JSON data, and all responses are returned as JSON data.

FaunaDB Headers

FaunaDB has a few custom headers used to modify API functionality, such as switching the API version on a per-request basis.

Header
X-FaunaDB-API-Version Override the API version for the request.
X-FaunaDB-Formatted-JSON If set to true, pretty-print JSON responses.

API Version Selection

As hinted above, it is possible to specify the API version on a per-request basis:


curl https://db.fauna.com/classes \
    -u fnACPOEi1HAAAOVFpciRvbPYkzMjXIgk33ArXIu3: \
    -H 'X-FaunaDB-API-Version: 2.0'

HTTP/1.1 200 OK
{ "resource": { "data": [ { "@ref": "classes/spells" } ] } }

CORS Support

FaunaDB supports Cross-origin resource sharing in order to enable direct usage of the API from browsers.

As FaunaDB’s security model is strict by default, it allows cross-origin resource sharing for any domain. CORS-related HTTP headers will be appropriately echoed, including those specified in an OPTIONS preflight request.

OPTIONS /{path}

Make a preflight request.

Header
Origin The CORS origin domain.
Access-Control-Request-Method In a CORS preflight request, the intended HTTP method.
Access-Control-Request-Headers In a CORS preflight request, any additional intended HTTP headers.

curl https://db.fauna.com/ \
    -u fnACPOEi1HAAAOVFpciRvbPYkzMjXIgk33ArXIu3: \
    -X OPTIONS \
    -H 'Origin: http://example.com'

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Headers: Authorization, Content-Type, Accept-Encoding, X-FaunaDB-API-Version
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: 
Access-Control-Max-Age: 86400

Authenticating Requests

FaunaDB uses HTTP Basic Auth in order to authenticate requests. All database-level requests must be authenticated with a key. The specific database and permissions that apply to a request are determined by the provided key.

Keys are always provided as the Basic Auth username.

Keys

Keys belong to one of four roles, either admin, server, server-readonly, or client.

Admin Keys

Keys with the admin role are used for managing databases and keys. An admin key can be used to create and destroy databases and keys. They should be very well protected.

Admin keys for cloud accounts are created in the admin keys interface. For enterprise customers the configured root key is used.

Server Keys

Keys with the server role bypass all permissions checks. Because they provide unrestricted access, they should be well-protected and only used in trusted or server-side environments.

Server Read-Only Keys

Keys with the server-readonly role allows read-only access to all data within the database it’s assigned to. Because they provide unrestricted access, they should be well-protected and only used in trusted or server-side environments.

Client Keys

Keys with the client role are restricted to actions and resources that are specifically marked with the public permission. Because their access is controlled, they are suitable for embedding in untrusted environments, such as mobile clients.

Typically they are used as part of an application’s user authentication flow, or to access a public data, such as an application’s logged-out view.

Token Access

Tokens allow direct access to FaunaDB by application users. Their use is optional. Each token has an associated instance. Requests made with a token apply resource-level permissions checks based on its instance.

Both server and client keys can generate tokens. Token generation with a client key requires a password. The password is optional when using a server key, but will be verified if provided.

Permissions

FaunaDB has a built-in permissions system that allows for fine-grained control over access to data.

Access is controlled by a resource’s permissions object. The identity of a request’s key is checked against the value of the permission field corresponding to the resource type and action being taken.

Permissions themselves may be set to one of the following values:

Value Access Allowed
empty An empty or missing permission means that only server keys are allowed.
“public” A permission set to the string “public” means that any key is allowed.
instance ref Only tokens belonging to the instance specified are allowed.
class ref Only tokens belonging to instances of the class are allowed.

Delegates

An instance may delegate access on its behalf to other instances by adding the other instances’ refs to its delegates list. Any token belonging to a member of delegates will be granted access as though they were the delegating instance.

For example, if classes/users/1 has read access to classes/spells/1 but classes/users/2 does not, classes/users/1 may grant access to classes/users/2 with the following query:


curl https://db.fauna.com/ \
    -u fnACPOEi1HAAAOVFpciRvbPYkzMjXIgk33ArXIu3: \
    -d '{
          "update": { "ref": { "class": "users" }, "id": 1 },
          "params": {
            "object": {
              "delegates": [ { "ref": { "class": "users" }, "id": 2 } ]
            }
          }
        }'

client.query(
  Update(
    Ref(Class(Value("users")), Value(1)),
    Obj("delegates", Arr(Ref(Class(Value("users")), Value(2))))));

client.query(
  q.Update(
    q.Ref(q.Class("users"), 1),
    { delegates: [q.Ref(q.Class("users"), 2)] }));

client.query(
  Update(
    Ref(Class("users"), 1),
    Obj("delegates" -> Arr(Ref(Class("users"), 2)))))

$client.query do
  update ref(class_('users'), 1),
         delegates: [ref(class_('users'), 2)]
end

client.Query(
  Update(
    Ref(Class("users"), 1),
    Obj("delegates", Arr(Ref(Class("users"), 2)))));

client.query(
  q.update(
    Ref(q.class_expr("users"), 1),
    {"delegates": [Ref(q.class_expr("users"), 2)]}
  ))

client.Query(
    f.Update(
        f.Ref(f.Class("users"), 1),
        f.Obj{"delegates": f.Arr{f.Ref(f.Class("users"), 2)}},
    ),
)

client.query(
    Update(
        ref: Ref(class: Class("users"), id: 1),
        to: Obj(
            "delegates" => Arr(Ref(class: Class("users"), id: 2))
        )
    )
)

Now, when classes/users/2 attempts to read classes/spells/1, they will be granted the same level of access as classes/users/1.

Delegates are not transitive–in the example above, classes/users/2 may not delegate classes/users/1’s permissions to another user.

Queries

The FaunaDB query language is the primary interface for interacting with FaunaDB Cloud or a FaunaDB Enterprise cluster.

While not a general purpose programming language, it provides much of the functionality expected from one. It allows for complex, precise manipulation and retrieval of data stored within FaunaDB.

The language comprises a collection of composable query expressions. All expressions return values, so it is easy, for example, to group multiple results together by combining them into an Array or Object, or map over a collection and compute a result–possibly fetching more data–for each member.

A query is executed by submitting it to a FaunaDB cluster, which computes and returns the result. Query execution is transactional: No changes are committed if something goes wrong. If a query fails, an error response is returned instead of a result.

Syntax

FaunaDB queries are written using a Lisp-like grammar that is serialized to JSON. JSON Objects are used to represent function calls and special types.


{ "function name": "argument", "parameter name": "argument" }

Other JSON data types are treated as literals.

The advantage of a JSON-based syntax is that FaunaDB queries may be generated via a client’s standard JSON manipulation library, eliminating an entire class of bugs related to malformed query strings.

Executing Queries

Queries are submitted to FaunaDB as JSON data over HTTP. Requests may be made via either GET or POST, though GET requests are required to be read-only.

Both GET and POST requests may include an optional ts parameter, which sets the global timestamp for the query. By default, it is the current time.

GET /

Execute the query passed via the q param. Queries made with GET are not allowed to modify data.

Parameters
q String required The query to perform. URL-encoded JSON.
ts Integer optional Microsecond UNIX timestamp. Default ts for all reads.

curl https://db.fauna.com/ \
    -u fnACPOEi1HAAAOVFpciRvbPYkzMjXIgk33ArXIu3: \
    -G \
    --data-urlencode q='{
          "paginate": {
            "match": { "index": "spells_by_element" },
            "terms": "fire"
          },
          "size": 5
        }'

HTTP/1.1 200 OK
{
  "resource": {
    "data": [
      { "@ref": "classes/spells/104979509692858368" },
      { "@ref": "classes/spells/104979509693618791" },
      { "@ref": "classes/spells/104979509694379214" }
    ]
  }
}

POST /

Execute the query passed via the request body.


curl https://db.fauna.com/ \
    -u fnACPOEi1HAAAOVFpciRvbPYkzMjXIgk33ArXIu3: \
    -d '{
          "paginate": {
            "match": { "index": "spells_by_element" },
            "terms": "fire"
          },
          "size": 5
        }'

HTTP/1.1 200 OK
{
  "resource": {
    "data": [
      { "@ref": "classes/spells/104979509692858368" },
      { "@ref": "classes/spells/104979509693618791" },
      { "@ref": "classes/spells/104979509694379214" }
    ]
  }
}

Objects and Sets

FaunaDB organizes data using an object-relational model. Data records are stored as objects. Object data is mapped into sets, which present a relational view of data more akin to tables in a traditional relational database system.

This hybrid approach means that application data can be modeled intuitively as objects without sacrificing the query flexibility provided by relational sets.

Objects

Each record in a FaunaDB database is stored as an object. All objects are instances of a specific class. This includes configuration as well: Databases, keys, user-defined classes, and indexes are all instances of their respective schema classes.

All instances have a set of common characteristics:

Every instance has an identifier called a ref. An instance’s ref encodes its class along with a unique id, and is therefore unique to that instance within the scope of the database in which it is stored.

All instances are manipulated with the same query language functions: get, create, update, replace, and delete. Instances returned by queries are represented as JSON objects. Within a query, an instance’s fields may be accessed using select.

Every instance has a set of common fields in addition to any other fields it contains. These common fields are:

Field Type
ref Ref The instance’s identifier. Unique within a database.
class Ref The ref of the instance’s class.
ts Integer The instance’s last updated time. Microsecond UNIX timestamp.

Sets

Sets are sorted collections of tuples. An index derives sets from instances of the classes in its source. As instances are created, modified, and deleted, sets are updated to reflect their instances’ current state.

Indexes are collections of sets, each of which has a natural key; a tuple of zero or more terms. The match query function constructs a set ref to identify a set for a given tuple of terms within an index:


curl https://db.fauna.com/ \
    -u fnACPOEi1HAAAOVFpciRvbPYkzMjXIgk33ArXIu3: \
    -d '{ "match": { "index": "spells_by_element" }, "terms": "water" }'

client.query(
  Match(Index(Value("spells_by_element")), Value("water")));

client.query(q.Match(q.Index("spells_by_element"), "water"));

client.query(Match(Index("spells_by_element"), "water"))

$client.query do
  match index('spells_by_element'), 'water'
end

client.Query(Match(Index("spells_by_element"), "water"));

client.query(q.match(q.index("spells_by_element"), "water"))

client.Query(f.MatchTerm(f.Index("spells_by_element"), "water"))

client.query(
    Match(index: Index("spells_by_element"), terms: "water")
)

HTTP/1.1 200 OK
{
  "resource": {
    "@set": {
      "match": { "@ref": "indexes/spells_by_element" },
      "terms": "water"
    }
  }
}

=> {
  "@set": {
    "match": { "@ref": "indexes/spells_by_element" },
    "terms": "water"
  }
}

=> {
  "@set": {
    "match": { "@ref": "indexes/spells_by_element" },
    "terms": "water"
  }
}

=> {
  "@set": {
    "match": { "@ref": "indexes/spells_by_element" },
    "terms": "water"
  }
}

=> {
  "@set": {
    "match": { "@ref": "indexes/spells_by_element" },
    "terms": "water"
  }
}

=> {
  "@set": {
    "match": { "@ref": "indexes/spells_by_element" },
    "terms": "water"
  }
}

=> {
  "@set": {
    "match": { "@ref": "indexes/spells_by_element" },
    "terms": "water"
  }
}

=> {
  "@set": {
    "match": { "@ref": "indexes/spells_by_element" },
    "terms": "water"
  }
}

=> {
  "@set": {
    "match": { "@ref": "indexes/spells_by_element" },
    "terms": "water"
  }
}

Set refs are unique according to their structure: Two set refs with the same structure refer to the same set within a database. Query functions such as union, intersection, and join allow the construction of more complex logical set identifiers:


curl https://db.fauna.com/ \
    -u fnACPOEi1HAAAOVFpciRvbPYkzMjXIgk33ArXIu3: \
    -d '{
          "intersection": [
            {
              "match": { "index": "spells_by_element" },
              "terms": "water"
            },
            {
              "match": { "index": "spells_by_element" },
              "terms": "fire"
            }
          ]
        }'

client.query(
  Intersection(
    Match(Index(Value("spells_by_element")), Value("water")),
    Match(Index(Value("spells_by_element")), Value("fire"))));

client.query(
  q.Intersection(
    q.Match(q.Index("spells_by_element"), "water"),
    q.Match(q.Index("spells_by_element"), "fire")));

client.query(
  Intersection(
    Match(Index("spells_by_element"), "water"),
    Match(Index("spells_by_element"), "fire")))

$client.query do
  intersection match(index('spells_by_element'), 'water'),
               match(index('spells_by_element'), 'fire')
end

client.Query(
  Intersection(
    Match(Index("spells_by_element"), "water"),
    Match(Index("spells_by_element"), "fire")));

client.query(
  q.intersection(
    q.match(q.index("spells_by_element"), "water"),
    q.match(q.index("spells_by_element"), "fire")
  ))

client.Query(
    f.Intersection(
        f.MatchTerm(f.Index("spells_by_element"), "water"),
        f.MatchTerm(f.Index("spells_by_element"), "fire"),
    ),
)

client.query(
    Intersection(
        Match(
            index: Index("spells_by_element"),
            terms: "water"
        ),
        Match(
            index: Index("spells_by_element"),
            terms: "fire"
        )
    )
)

HTTP/1.1 200 OK
{
  "resource": {
    "@set": {
      "intersection": [
        {
          "@set": {
            "match": { "@ref": "indexes/spells_by_element" },
            "terms": "water"
          }
        },
        {
          "@set": {
            "match": { "@ref": "indexes/spells_by_element" },
            "terms": "fire"
          }
        }
      ]
    }
  }
}

=> {
  "@set": {
    "intersection": [
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "water"
        }
      },
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "fire"
        }
      }
    ]
  }
}

=> {
  "@set": {
    "intersection": [
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "water"
        }
      },
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "fire"
        }
      }
    ]
  }
}

=> {
  "@set": {
    "intersection": [
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "water"
        }
      },
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "fire"
        }
      }
    ]
  }
}

=> {
  "@set": {
    "intersection": [
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "water"
        }
      },
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "fire"
        }
      }
    ]
  }
}

=> {
  "@set": {
    "intersection": [
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "water"
        }
      },
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "fire"
        }
      }
    ]
  }
}

=> {
  "@set": {
    "intersection": [
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "water"
        }
      },
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "fire"
        }
      }
    ]
  }
}

=> {
  "@set": {
    "intersection": [
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "water"
        }
      },
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "fire"
        }
      }
    ]
  }
}

=> {
  "@set": {
    "intersection": [
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "water"
        }
      },
      {
        "@set": {
          "match": { "@ref": "indexes/spells_by_element" },
          "terms": "fire"
        }
      }
    ]
  }
}

The paginate function is used to retrieve the tuples of a set. The Page object returned by paginate contains an array of tuples and cursors for moving forward or backward within the set.

Field Type
data Array The elements in the page.
after Cursor The cursor for the next page, inclusive. Optional.
before Cursor The cursor for the previous page, exclusive. Optional.

Pages can be further manipulated using collection-oriented functions such as map and filter, or individual elements can be extracted using select.

Error Responses

FaunaDB will return structured error responses to failed requests in the form of a JSON object with a top-level errors key. errors will contain an array of error objects with the following fields:

Name Type
code String Classification of the failure.
description String Additional information about the failure.
position Array JS path of the form in error.

Invalid Expressions

The “invalid expression” code is returned when a malformed request is received.

An “unbound variable” code is returned when an expression attempts to access a variable which has not been declared in a let expression in scope.

Invalid Arguments

The “invalid argument” code is returned when an argument of unexpected type is provided to a query function.

Invalid Permissions

The “permission denied” code is returned when an unauthorized request is attempted. The “authentication failed” code is returned when an authentication attempt fails.

Missing or Conflicting Data

The “instance not found” code is returned when a record is missing from the database.

The “value not found” code is returned when a request attempts to access an invalid JS path (for instance via the select function).

The “instance already exists” code is returned when a primary key conflict occurs

Invalid Data

Attempts to insert invalid data will result in a top-level error with the “validation failed” code. In addition to the common error object fields, the object will also contain failures–an array of error objects describing the invalid data fields. These validation error objects will contain the following fields:

Name Type
code String Classification of the failure.
description String Additional information about the failure.
field Array JS path within the data of the field in error.

The “invalid type” code is returned when a request attempts to insert data of incorrect type.

The “value required” code is returned when a required field is missing.

The “duplicate value” code is returned when a secondary uniqueness constraint fails.

The “invalid reference” code is returned when a reference is malformed, access is unauthorized, or not found in the database.

The “bounds error” code is returned when a field’s value is not within the expected range.