Four Key Reasons to Learn Markdown
Back-End Leveling UpWriting documentation is fun—really, really fun. I know some engineers may disagree with me, but as a technical writer, creating quality documentation that will...
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.
To get started with FaunaDB, sign up for a new account. Then, you will
Sign Up for a free FaunaDB Account. As of September 2020, FaunaDB offers a generous free tier to get started with.
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.
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.
Navigate to the Web Shell for your new database using the side navigation by clicking the >_ Shell
link.
Use 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
.
A 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" })
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, andname
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" }
A 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.
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, anddata
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.
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: {}, })
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,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 field
. field
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, andpartitions
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 }
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") )
This response object has one field:
data
field with an array of references to the matched documents.{ data: [Ref(Collection("users"), "274944144667836946")] }
A 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") )
This response object has 3 fields:
ref
field whose value is a reference to the user document,ts
field with an integer timestamp value, anddata
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" } }
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.
FaunaDB has a lot more to offer. Here are a few more highlights for further exploration.
In addition to the built-in FQL functions, you can create your own User-defined functions to extend FQL or encapsulate logic.
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).
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.
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.
Writing documentation is fun—really, really fun. I know some engineers may disagree with me, but as a technical writer, creating quality documentation that will...
Humanity has come a long way in its technological journey. We have reached the cusp of an age in which the concepts we have...
Go 1.18 has finally landed, and with it comes its own flavor of generics. In a previous post, we went over the accepted proposal and dove...