Search

Intro to FaunaDB and FQL

Richard Flosi

6 min read

Sep 8, 2020

Intro to FaunaDB and FQL

To start, FaunaDB is a general-purpose database that combines relationships with document flexibility and has core functions that are inspired by the clockless, strictly-serializable transactional protocol for multi-region environments, called Calvin. In other words, it’s “a serverless, globally distributed, cloud database as a service.” A FaunaDB Database contains Collections of Documents. (Throughout the post I’ll make reference to a number of terms. Here’s a handy FaunaDB Cheat Sheet for reference. It might be worth having this open in a separate tab as you read through the post). Now, let’s dive into the details.

Getting Started

To get started with FaunaDB, sign up for a new account. Then, you will

  • create your first database,
  • create a collection in your database,
  • add a document to your collection, and
  • index the documents in the collection.

Sign Up

Sign Up for a free FaunaDB Account. As of September 2020, FaunaDB offers a generous free tier to get started with.

Fauna sign up form

Create a Database

Visit the Cloud Dashboard. Click to Create a New Database. Name the database www. Do not check the box to “Pre-populate with demo data”; you will be adding your own data shortly.

create a new database in faunaQuerying the Database

Queries to FaunaDB are written in Fauna Query Language (FQL), a functional query language. Fauna offers client drivers in various languages allowing you to interact with FaunaDB from your codebase. But for this blog post, you’ll be writing all FQL in your browser using FaunaDB’s Web Shell which uses the JavaScript driver.

Open the Web Shell

Navigate to the Web Shell for your new database using the side navigation by clicking the >_ Shell link.

Fauna web shellUse the Web Shell to execute your FQL by entering each query into the black input console on the bottom of the Web Shell and clicking Run Query.

Create a Collection

Collection is similar to a table in a relational database. It has a unique name, like “users,” and holds Documents, like the details of each user.

Create your first Collection called users from the Web Shell using the CreateCollection() function with the following FQL query:

CreateCollection({ name: "users" })
create collection in Fauna web shell

This response object contains four fields:

  • ref field with a value of Collection("users") which is used to create relationships with the collection,
  • ts field with an integer value representing the timestamp in UTC with nanosecond precision,
  • history_days field defaulting to 30 days which is the number of days to retain document history, and
  • name field with a value of "users" which you provided as the logical name for the collection.
{
  ref: Collection("users"),
  ts: 159846586441000,
  history_days: 30,
  name: "users"
}

Create a Document

Document is like a row, and like the abstraction that is the “row,” a document can be used to model almost anything. Even your newly-created www database and users collection are Documents.

Add a user document to your "users" collection by passing the Collection’s name and the Document’s param_object with credentials and data fields to the Create() function:

Create("users", {
  credentials: { password: "F4un4I$Fun" },
  data: { name: "Richard", email: "rflosi@bignerdranch.com" }
})

The credentials object includes a password which is stored as a BCrypt hash in FaunaDB. Providing credentials with a password is one way to allow users to authenticate using the Login() function.

The data object is a schemaless JSON object for your document with name and email fields. You can provide any fields you like here.

Fauna web shell create document This response object has three fields:

  • ref field whose value includes the Collection("users") ref and a unique document id,
  • ts field with an integer timestamp value, and
  • data field containing your document data which includes user name and email address fields.
{
  ref: Ref(Collection("users"), "274944144667836946"),
  ts: 1598466019230000,
  data: {
    name: "Richard",
    email: "rflosi@bignerdranch.com"
  }
}

NOTE: the credentials you provided will NOT be returned.

Create an Index

Indexes allow for the organization and retrieval of documents by attributes other than their references. When you create an index, you specify its source, which is one or more collections of documents. An Index allows you to define uniqueness constraints and facilitates efficient data lookup.

Create a unique Index of our users by email address using the CreateIndex() function:

CreateIndex({
  name: "users_by_email",
  source: Collection("users"),
  terms: [{ field: [ "data", "email" ] }],
  unique: true,
  permissions: {},
})
fauna web shell create index

This response object has nine fields:

  • ref field whose value is Index("users_by_email") which you’ll use to query by email address,
  • ts field with an integer timestamp value,
  • an active field with a value of true which indicates if the index has completed building,
  • serialized field with a value of true which indicates writes are serialized with concurrent reads and writes,
  • name field with the value "users_by_email", which you provided as the logical name for your index,
  • source field with a value of Collection("users") which defines which collection(s) to index,
  • terms field whose value is an array with one object with a key of fieldfield is an array that defines the path to the data you want to index. In this case, you are indexing the email field under the data object, thus the path is ["data", "email"],
  • unique field with a value of true which ensures that the indexed terms will be unique to the collection, and
  • partitions field with a value of 1 which represents the number of sets defined by terms used to improve index performance.
{
  ref: Index("users_by_email"),
  ts: 1598466147239300,
  active: true,
  serialized: true,
  name: "users_by_email",
  source: Collection("users"),
  terms: [
    {
      field: ["data", "email"]
    }
  ],
  unique: true,
  partitions: 1
}

Get user by email address

Lookup a user by email address using our users_by_email Index with the Match() function and use the Paginate() function to get the first page of results from the matched Set. Use the following query to retrieve the first page of user references:

Paginate(
  Match(Index("users_by_email"),  "rflosi@bignerdranch.com")
)

fauna web shell match indexThis response object has one field:

  • data field with an array of references to the matched documents.
{
  data: [Ref(Collection("users"), "274944144667836946")]
}

Ref() is similar to a foreign key in a relational database and is how relationships are represented in FaunaDB. Calling the Get() function on our Ref() object causes FaunaDB to return the referenced Document. Calling the Get() function on a Match() result will retrieve the first Document from that result set. Use the following query to retrieve the user document:

Get(
  Match(Index("users_by_email"), "rflosi@bignerdranch.com")
)

fauna web shell get matchThis response object has 3 fields:

  • ref field whose value is a reference to the user document,
  • ts field with an integer timestamp value, and
  • data field whose value is an object containing the user name and email address.
{
  ref: Ref(Collection("users"), "274944144667836946"),
  ts: 1598466019230000,
  data: {
    name: "Richard",
    email: "rflosi@bignerdranch.com"
  }
}

Summary

In this post, you created a new database called www, added a collection called users which acts as a table, added a document to the collection which acts as a row in the table thus creating an instance of a user, and created an index of users by their unique email addresses.

Next Steps

FaunaDB has a lot more to offer. Here are a few more highlights for further exploration.

User-defined functions

In addition to the built-in FQL functions, you can create your own User-defined functions to extend FQL or encapsulate logic.

Built-in User Authentication and Authorization

As mentioned, the password defined in credentials will allow you to authenticate using the Login() function.

You may have noticed the use of permissions: {} when you created your index. permissions indicates who is allowed to read the index. By default, everyone can read the index. Setting permissions to an empty object removes all permissions; a server key, all-powerful, is then required to access the index. However, configuring permissions in this way has been deprecated in favor of User-defined roles and Attribute-based access control (ABAC).

Comparison to other databases

If you are familiar with SQL databases, you might want to explore Fauna Query Language for SQL users to better understand how SQL queries translate to FQL queries.

If you are familiar with NoSQL databases, you might want to compare MongoDB and FaunaDB or DynamoDB and FaunaDB.

GraphQL Support

As an alternative to creating your collections and indexes with FQL, you can import a GraphQL schema which will be translated into the equivalent FaunaDB collections and indexes. Once a GraphQL schema is defined, you can query your database with GraphQL. FaunaDB’s GraphQL endpoint will translate your GraphQL queries into FQL and return results in JSON.

Richard Flosi

Author Big Nerd Ranch
Richard Flosi brings 20+ years of web development experience to Big Nerd Ranch. Richard is passionate about using full-stack JavaScript serverless architecture to build scalable long-term solutions.
Speak with a Nerd

Schedule a call today! Our team of Nerds are ready to help

Let's Talk

Related Posts

We are ready to discuss your needs.

Not applicable? Click here to schedule a call.

Stay in Touch WITH Big Nerd Ranch News