Overview

This is a cookbook of examples that demonstrate various Fauna queries, including Databases, Keys, Collections, User-defined functions, and Indexes.

Databases

Reading or writing database definitions requires an admin key.

Create a database

Copied!
var adminClient = new faunadb.Client({
  secret: adminKey
})

adminClient.query(
  q.({ name: 'annuvin' })
)
.then((ret) => console.log(ret))
{ ref: (id=annuvin, collection=(id=databases)),
  ts: 1520223296616581,
  name: 'annuvin' }

Paginate all databases

Copied!
adminClient.query(
  q.(q.())
)
.then((ret) => console.log(ret))
{ data:
   [ (id=prydain, collection=(id=databases)),
     (id=caledonia, collection=(id=databases)),
     (id=annuvin, collection=(id=databases)) ] }

Get a database

Copied!
adminClient.query(
  q.(q.('annuvin'))
)
.then((ret) => console.log(ret))
{ ref: (id=annuvin, collection=(id=databases)),
  ts: 1520223296616581,
  name: 'annuvin' }

Rename a database

Copied!
adminClient.query(
  q.(q.('annuvin'), { name: 'llyr' })
)
.then((ret) => console.log(ret))
{ ref: (id=llyr, collection=(id=databases)),
  ts: 1520223296736896,
  name: 'llyr' }

Annotate a database

Copied!
adminClient.query(
  q.(q.('llyr'), { data: { env: 'test' } })
)
.then((ret) => console.log(ret))
{ ref: (id=llyr, collection=(id=databases)),
  ts: 1527631840251296,
  name: 'llyr',
  data: { env: 'test' } }

Delete a database

Copied!
adminClient.query(
  q.(q.('llyr'))
)
.then((ret) => console.log(ret))
{ ref: (id=llyr, collection=(id=databases)),
  ts: 1527631840251296,
  name: 'llyr',
  data: { env: 'test' } }

Keys

Reading or writing keys requires an admin key.

Create a key

Copied!
var adminClient = new faunadb.Client({
  secret: adminKey
})

adminClient.query(
  q.({
    database: q.('caledonia'),
    role: 'server',
  })
)
.then((ret) => console.log(ret))
{ ref: (id=200669511526908416, collection=(id=keys)),
  ts: 1527632209151020,
  database: (id=caledonia, collection=(id=databases)),
  role: 'server',
  secret: 'fnACyOvbh9ACAOtRQDQSefokn4iuMf9zn_FakPAx',
  hashed_secret: '$2a$05$dszFRic/tkKzsG0rrUj3Fu7.nSoYuJL7BdnHXepx4XTzfcWtVEZRC' }

Paginate all keys

Copied!
adminClient.query(q.(q.()))
.then((ret) => console.log(ret))
{ data:
   [ (id=192900703858983424, collection=(id=keys)),
     (id=192900703888343552, collection=(id=keys)) ] }

Get a key

Copied!
adminClient.query(
  q.(q.(q.(), '192900703888343552'))
)
.then((ret) => console.log(ret))
{ ref: (id=192900703888343552, collection=(id=keys)),
  ts: 1527632209151020,
  database: (id=caledonia, collection=(id=databases)),
  role: 'server',
  hashed_secret: '$2a$05$dszFRic/tkKzsG0rrUj3Fu7.nSoYuJL7BdnHXepx4XTzfcWtVEZRC' }

Delete a key

Copied!
adminClient.query(q.(q.(q.(), '192900703858983424')))
  .then((ret) => console.log(ret))
{ ref: (id=192900703858983424, collection=(id=keys)),
  ts: 1527632209151020,
  database: (id=caledonia, collection=(id=databases)),
  role: 'server',
  hashed_secret: '$2a$05$dszFRic/tkKzsG0rrUj3Fu7.nSoYuJL7BdnHXepx4XTzfcWtVEZRC' }

Collections

Reading or writing collections requires a server key.

Create a collection

Copied!
client.query(
  q.({ name: 'spells' })
)
.then((ret) => console.log(ret))
{ ref: (id=spells, collection=(id=collections))
  ts: 1520223297210738,
  history_days: 30,
  name: 'spells' }

Paginate all collections

Copied!
client.query(
  q.(q.())
)
.then((ret) => console.log(ret))
{ data:
   [ (id=decrepit_huts, collection=(id=collections)),
     (id=spellbooks, collection=(id=collections)),
     (id=characters, collection=(id=collections)),
     (id=spells, collection=(id=collections)) ] }

Get a collection

Copied!
client.query(
  q.(q.('spells'))
)
.then((ret) => console.log(ret))
{ ref: (id=spells, collection=(id=collections))
  ts: 1520223297210738,
  history_days: 30,
  name: 'spells' }

Rename a collection

Copied!
client.query(
  q.(
    q.('decrepit_huts'),
    { name: 'dilapidated_huts' },
  )
)
.then((ret) => console.log(ret))
{ ref: (id=dilapidated_huts, collection=(id=collections)),
  ts: 1520223297276685,
  history_days: 30,
  name: 'dilapidated_huts' }

Delete a collection

Copied!
client.query(
  q.(q.('dilapidated_huts'))
)
.then((ret) => console.log(ret))
{ ref: (id=dilapidated_huts, collection=(id=collections)),
  ts: 1520223297276685,
  history_days: 30,
  name: 'dilapidated_huts' }

Create a document in a collection

Copied!
client.query(
  q.(
    q.('spells'),
    {
      data: {
        name: 'Fire Beak',
        element: ['air', 'fire'],
      },
    },
  )
)
.then((ret) => console.log(ret))
{ ref: (id=200669801878651393, collection=(id=spells, collection=(id=collections))),
  ts: 1527632486061801,
  data: { name: 'Fire Beak', element: [ 'air', 'fire' ] } }

User-defined functions

Creating and updating a function requires:

Create a user-defined function

Let’s create a function to allow our authors to create new blog posts. We’ll set it up to allow the author to provide a title and body. This way authors are constrained by what data they can store on a post document.

Copied!
client.query(
  q.({
    name: 'create_entry',
    body: q.(
      q.(
        ['title', 'body'],
        q.(
          q.('posts'),
          {
            data: {
              title: q.('title'),
              body: q.('body'),
            },
          },
        ),
      )
    ),
    permissions: { call: 'public' },
    role: 'server',
  })
)
  .then((ret) => console.log(ret))
{ ref: (id=create_entry, collection=(id=functions)),
  ts: 1527633126019327,
  name: 'create_entry',
  body: ("[object Object]"),
  permissions: { call: 'public' },
  role: 'server' }

Create credentials, login, and call a user-defined function

Now we can create a new author and use their credentials.

Copied!
client.query(
  q.(
    q.('authors'),
    {
      data: { name: 'Patty Smith' },
      credentials: { password: '123456' },
    }
  )
)
.then((ret) => console.log(ret))
{ ref: (id=192900738957967872, collection=(id=authors, collection=(id=collections))),
  ts: 1527633688652829,
  data: { name: 'Patty Smith' } }
Copied!
client.query(
  q.(
    q.(q.('authors'), '192900738957967872'),
    { password: '123456' }
  )
)
.then((ret) => console.log(ret))
{ ref: (id=192900739000959488, collection=(id=tokens)),
  ts: 1520223330375483,
  document: (id=192900738957967872, collection=(id=authors, collection=(id=collections))),
  secret: 'fnECrVIzORACAAKtUir3AAIAq-5ZBLtxuAEhY0cju64xXi_HUhg' }

The value in the secret field is the authentication token representing the author that we just created. A new client connection object needs to be created using this secret, so that subsequent queries are performed as the author. See the User authentication tutorial for more details.

The following query calls the create_entry function that we created earlier:

Copied!
client.query(
  q.(
    q.('create_entry'),
    [ 'First Post Title', 'This is my first blog post!' ],
  )
)
  .then((ret) => console.log(ret))
{ ref: (id=192900739108962816, collection=(id=posts, collection=(id=collections))),
  ts: 1520223330453871,
  data:
   { title: 'First Post Title',
     body: 'This is my first blog post!' } }

Indexes

Creating and updating an index requires a server key.

Create an index

Let’s create an index to list all spells' names:

Copied!
client.query(
  q.(
    {
      name: 'all_spell_names',
      source: q.('spells'),
      values: [{ field: ['data', 'name'] }],
    },
  )
)
.then((ret) => console.log(ret))
{ ref: (id=all_spell_names, collection=(id=indexes)),
  ts: 1527625252080602,
  active: false,
  partitions: 8,
  name: 'all_spell_names',
  source: (id=spells, collection=(id=collections)),
  values: [ { field: [Array] } ] }

It’s also possible to specify the terms that are going to be used to locate entries on the index. In this case, we’ll create an index that allow us to find spells by their elements:

Copied!
client.query(
  q.(
    { name: 'spells_by_element_with_name',
      source: q.('spells'),
      terms: [{ field: ['data', 'element'] }],
      values: [{ field: ['data', 'name'] }],
    },
  )
)
.then((ret) => console.log(ret))
{ ref: (id=spells_by_element_with_name, collection=(id=indexes)),
  ts: 1527625636424120,
  active: false,
  partitions: 1,
  name: 'spells_by_element_with_name',
  source: (id=spells, collection=(id=collections)),
  terms: [ { field: [Array] } ],
  values: [ { field: [Array] } ] }

Indexes can also cover multiple values, which affect the order of their results. See the ordering section for more information.

The length of the field values specified for the terms or values fields must not exceed 32k bytes. The maximum size of an index entry, which is comprised of the terms and values content (and some overhead to distinguish multiple fields), must not exceed 64k bytes. If an index entry is too large, the query that created/updated the index entry fails.

Here is an example of an index that allows you to search for a spell by its element and get back both the spell name and ref:

Copied!
client.query(
  q.(
    {
      name: 'spells_with_ref_by_element_name',
      source: q.('spells'),
      terms: [{ field: ['data', 'element'] }],
      values: [{ field: ['data', 'name'] }, { field: ['ref'] }],
    },
  )
)
.then((ret) => console.log(ret))
{ ref: (id=spells_with_ref_by_element_name, collection=(id=indexes)),
  ts: 1527625316880457,
  active: false,
  partitions: 1,
  name: 'spells_with_ref_by_element_name',
  source: (id=spells, collection=(id=collections)),
  terms: [ { field: [Array] } ],
  values: [ { field: [Array] }, { field: [Array] } ] }

Paginate all indexes

Copied!
client.query(q.(q.()))
  .then((ret) => console.log(ret))
{ data: [
    (id=all_spells, collection=(id=indexes)),
    (id=spells_by_name, collection=(id=indexes)),
    (id=spells_by_element, collection=(id=indexes)),
    (id=spells_by_element_and_name, collection=(id=indexes)),
    (id=spellbooks_by_owner, collection=(id=indexes)),
    (id=spells_by_spellbook, collection=(id=indexes)),
    (id=all_spell_names, collection=(id=indexes)),
    (id=spells_by_element_with_name, collection=(id=indexes)),
    (id=spells_with_ref_by_element_name, collection=(id=indexes)),
    (id=latest_spells_by_element, collection=(id=indexes)) ] }

Get an index

Copied!
client.query(
  q.(q.('spells_by_element_with_name'))
)
.then((ret) => console.log(ret))
{ ref: (id=spells_by_element_with_name, collection=(id=indexes)),
  ts: 1527625636424120,
  active: false,
  partitions: 1,
  name: 'spells_by_element_with_name',
  source: (id=spells, collection=(id=collections)),
  terms: [ { field: [Array] } ],
  values: [ { field: [Array] } ] }

Query an index

Indexes created without specifying the terms field return all indexed values on the indexed collection.

Copied!
client.query(
  q.(q.(q.('all_spell_names')))
)
.then((ret) => console.log(ret))
{ data: [ "Fire Beak", "Hippo's Wallow", "Water Dragon's Claw" ] }

For indexes where the terms field was defined, entries are located using the indexed terms.

Copied!
client.query(
  q.(
    q.(q.('spells_by_element_with_name'), 'fire')
  )
)
.then((ret) => console.log(ret))
{ data: [ "Fire Beak", "Water Dragon's Claw" ] }

Indexes return all their values field values. Here we’re searching for all of the spells with element fire, using an index that returns both the spell name and its reference:

Copied!
client.query(
  q.(
    q.(q.('spells_with_ref_by_element_name'), 'fire')
  )
)
.then((ret) => console.log(ret))
{ data: [
    [ "Fire Beak",
      (id=192900707573039616, collection=(id=spells, collection=(id=collections))) ],
    [ "Water Dragon's Claw",
      (id=192900707595059712, collection=(id=spells, collection=(id=collections))) ] }

Indexes with ordering and transformations

Let’s create an index to return the latest spells updated. To do this we will create an index which is sorted reversely by each spell’s last updated timestamp.

We’ll specify terms on this index so we can find spells by element. We’ll also use the casefold function as a transformation to the specified term which will make the entries lowercase in the index.

Copied!
client.query(
  q.(
    {
      name: 'latest_spells_by_element',
      source: q.('spells'),
      terms: [{ field: ['data', 'element'], transform: 'casefold' }],
      values: [
        { field: ['ts'], reverse: true },
        { field: ['data', 'name'] },
        { field: ['ref'] },
      ],
    },
  )
)
.then((ret) => console.log(ret))
{ ref: (id=latest_spells_by_element, collection=(id=indexes)),
  ts: 1527625370644230,
  active: false,
  partitions: 1,
  name: 'latest_spells_by_element',
  source: (id=spells, collection=(id=collections)),
  terms: [ { field: [Array], transform: 'casefold' } ],
  values:
   [ { field: [Array], reverse: true },
     { field: [Array] },
     { field: [Array] } ] }

When querying the index, you can use the casefold function to convert the query terms to lowercase. Using casefold in a query to an index configured with casefold essentially makes a case-insensitive query.

Copied!
client.query(
  q.(
    q.(
      q.('latest_spells_by_element'),
      q.('FIRE'),
    )
  )
)
.then((ret) => console.log(ret))
{ data:
   [ [ 1520223300442183,
       "Water Dragon's Claw",
       (id=192900707595059712, collection=(id=spells, collection=(id=collections))) ],
   [ [ 1520223300419731
       "Fire Beak",
       (id=192900707573039616, collection=(id=spells, collection=(id=collections))) ] ] }

Since timestamp was added to the index to facilitate sorting, it might not be required for the query to return timestamp values to the application layer. In that case, it’s possible to use the map function to extract only the desired fields.

Copied!
client.query(
  q.(
    q.(
      q.(
        q.('latest_spells_by_element'),
        q.('FIRE'),
      )
    ),
    q.(['_', 'name', 'ref'], [q.('name'), q.('ref')]),
  )
)
.then((ret) => console.log(ret))
{ data:
   [ [ "Water Dragon's Claw",
       (id=192900707595059712, collection=(id=spells, collection=(id=collections))) ],
     [ "Fire Beak",
       (id=192900707573039616, collection=(id=spells, collection=(id=collections))) ] ] }

Update an index

It is disallowed to update an index in a way that changes its shape. This means no changing the source or the terms. You can, however, change the name of an index or its uniqueness. If you update the unique field, it does not remove existing duplicated items from the index.

Copied!
client.query(
  q.(
    q.('spells_by_element_with_name'),
    { name: 'spells_by_kind' },
  )
)
.then((ret) => console.log(ret))
{ ref: (id=spells_by_kind, collection=(id=indexes)),
  ts: 1527630282459547,
  active: true,
  partitions: 1,
  name: 'spells_by_kind',
  source: (id=spells, collection=(id=collections)),
  terms: [ { field: [Array] } ],
  values: [ { field: [Array] } ] }

Delete an index

Copied!
client.query(
  q.(q.('spells_by_kind'))
)
.then((ret) => console.log(ret))
{ ref: (id=spells_by_kind, collection=(id=indexes)),
  ts: 1527625346513000,
  active: true,
  partitions: 1,
  name: 'spells_by_kind',
  source: (id=spells, collection=(id=collections)),
  terms: [ { field: [Array] } ],
  values: [ { field: [Array] }, { field: [Array] } ] }

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!