Create, retrieve, update, and delete documents in FaunaDB

FaunaDB allows you to store documents and query them in a relational fashion. This section walks you through a basic example of creating, retrieving, updating, and deleting (CRUD) documents in FaunaDB, including working with collections. If you are new to FaunaDB, make sure to check out our Glossary for definitions.

Introduction

To demonstrate how to perform CRUD operations in FaunaDB, we are going to use the example of blog posts: creating blog posts, updating them with additional attributes, and querying for specific posts.

The steps are:

We have set up this example so you can follow along from start to finish. Feel free to skip straight to Create a post if you are just looking for examples of the create, retrieve, update, and delete process.

Requirements

This section walks you through setting up your environment, installing a driver, importing the driver, obtaining an admin API key, and instantiating the client.

Supported runtimes

Before you install the driver, it’s important to ensure you’re running a compatible version of the language runtime and have satisfied other dependencies.

The JavaScript driver supports and is tested on:

  • Node.js

    • LTS (v4)

    • Stable (v6)

    • v0.12.x

  • Chrome

  • Firefox

  • Safari

  • Internet Explorer 11

Install the driver

Node.js

To install the JavaScript driver, run this in the terminal:

npm install --save faunadb

See faunadb on NPM for more information.

Browsers

The browser release can be found in the fauna/faunadb-js-release repository.

This release can be installed via bower:

bower install faunadb

Or via CDN:

<script src="//cdn.jsdelivr.net/gh/fauna/faunadb-js-release@latest/faunadb.js"></script>

The minified version of the driver can also be used via CDN:

<script src="//cdn.jsdelivr.net/gh/fauna/faunadb-js-release@latest/faunadb-min.js"></script>

Import the driver

var faunadb = require('faunadb'),
  q = faunadb.query;

This is the recommended require stanza. The faunadb.query module contains all of the functions to create FaunaDB Query expressions.

Similarly with es6 modules:

import faunadb, { query as q } from "faunadb"

The CDN package exposes a global faunadb variable.

Obtain an admin API key

To follow along, you need an admin API key, which you can create using the FaunaDB Console.

Instantiate the client

var client = new faunadb.Client({ secret: 'YOUR_FAUNADB_SECRET' });

Create a database

Create a database in FaunaDB:

client.query(
  q.CreateDatabase({ name: 'my_app' })
)
.then((ret) => console.log(ret))
{ ref: Ref(id=my_app, collection=Ref(id=databases)),
  ts: 1528127545620042,
  name: 'my_app' }

Accessing the database

Create an initial server key by using an admin key. The server key has unrestricted access to a single database; in this case, the server key will only access the blog post database we just created.

client.query(
  q.CreateKey({
    database: q.Database('my_app'),
    role: 'server',
  })
)
.then((ret) => console.log(ret))
{ ref: Ref(id=201188951594107392, collection=Ref(id=keys)),
  ts: 1528127585813144,
  database: Ref(id=my_app, collection=Ref(id=databases)),
  role: 'server',
  secret: 'fnACysRJGIACAHiL_5f0UxHlPFIZgq876ptMNJ72',
  hashed_secret: '$2a$05$wm6Zwl8dhlY3hFbJj0JrHuSt4YszzsDRd9KdxVbxg98yVOMJiEp0i' }

Set up a collection

FaunaDB stores data in the form of nested containers. A database contains collections, and collections contain documents. Each document belongs to a specific collection. So in order to create a document for a post, we need to first create a collection for posts.

Create a collection using the CreateCollection function with a param_object containing the name of the collection. We shall name our collection "posts":

client.query(
  q.CreateCollection({ name: 'posts' })
)
.then((ret) => console.log(ret))
{ ref: Ref(id=posts, collection=Ref(id=collections)),
  ts: 1527349751848648,
  history_days: 30,
  name: 'posts' }

Create an index

Before we create any document, let’s ensure that we can easily access them. We do this by creating an index using the CreateIndex function. We create this index now to help make the examples clear; in production, you can create an index at any time.

The customary way to access documents within a collection is by specifying a criteria for one of the fields. To enable criteria-based searches, we need to first create an index using the path of the field within the document.

Create an index on the post’s title:

client.query(
  q.CreateIndex({
    name: 'posts_by_title',
    source: q.Collection('posts'),
    terms: [{ field: ['data', 'title'] }],
  })
)
.then((ret) => console.log(ret))
{ ref: Ref(id=posts_by_title, collection=Ref(id=indexes)),
  ts: 1527350163658503,
  active: false,
  partitions: 1,
  name: 'posts_by_title',
  source: Ref(id=posts, collection=Ref(id=collections)),
  terms: [ { field: [Array] } ] }

It is also possible to specify the values of the document that should be returned when querying the index. We also create an index for a post’s tags:

client.query(
  q.CreateIndex({
    name: 'posts_by_tags_with_title',
    source: q.Collection('posts'),
    terms: [{ field: ['data', 'tags'] }],
    values: [{ field: ['data', 'title'] }],
  })
)
.then((ret) => console.log(ret))
{ ref: Ref(id=posts_by_tags_with_title, collection=Ref(id=indexes)),
  ts: 1527350198235760,
  active: false,
  partitions: 1,
  name: 'posts_by_tags_with_title',
  source: Ref(id=posts, collection=Ref(id=collections)),
  terms: [ { field: [Array] } ],
  values: [ { field: [Array] } ] }

Create a post

Posts are created by calling the Create function with the ref of the posts collection and a param_object that specifies the structure of the document to be created, as well as permissions for the document. The post’s data is included in the param_object's data field.

client.query(
  q.Create(
    q.Collection('posts'),
    { data: { title: 'What I had for breakfast ..' } },
  )
)
.then((ret) => console.log(ret))
{ ref:
   Ref(id=200372984285757952, collection=Ref(id=posts, collection=Ref(id=collections))),
  ts: 1527349418742172,
  data: { title: 'What I had for breakfast ..' } }

The Create function returns the post document just created. As you can see in the output, the ref of the document is an automatically-generated identifier that is unique to the document within its database. You can specify an id to use instead of the auto-generated id, but it must be unique.

Create several posts

It can quickly become tedious to repeat the Create function for multiple posts.

This is where FaunaDB’s transaction language really shines. Let’s use a Map function to create several posts at once:

client.query(
  q.Map(
    [
      'My cat and other marvels',
      'Pondering during a commute',
      'Deep meanings in a latte',
    ],
    q.Lambda(
      'post_title',
      q.Create(
        q.Collection('posts'),
        { data: { title: q.Var('post_title') } },
      )
    ),
  )
)
.then((ret) => console.log(ret))
[ { ref:
     Ref(id=200373080062689792, collection=Ref(id=posts, collection=Ref(id=collections))),
    ts: 1527349510087794,
    data: { title: 'My cat and other marvels' } },
  { ref:
     Ref(id=200373080062690816, collection=Ref(id=posts, collection=Ref(id=collections))),
    ts: 1527349510087794,
    data: { title: 'Pondering during a commute' } },
  { ref:
     Ref(id=200373080062691840, collection=Ref(id=posts, collection=Ref(id=collections))),
    ts: 1527349510087794,
    data: { title: 'Deep meanings in a latte' } } ]

Using the Map function, we can restructure the data as an array and wrap the Create in a lambda function, which then runs over each document in the collection. The anonymous lambda function specifies a variable post_title which is used as a placeholder in the parameters sent to the create function. This way, multiple documents in a collection can be created using a single query.

Retrieve posts

The easiest way to retrieve a document is by using its reference value:

client.query(
  q.Get(q.Ref(q.Collection('posts'), '192903209792046592'))
)
.then((ret) => console.log(ret))
{ ref:
   Ref(id=192903209792046592, collection=Ref(id=posts, collection=Ref(id=collections))),
  ts: 1527350638301882,
  data: { title: 'My cat and other marvels' } }

You can query for posts with a specific title using the match function and the index we created earlier:

client.query(
  q.Get(
    q.Match(q.Index('posts_by_title'), 'My cat and other marvels')
  )
)
.then((ret) => console.log(ret))
{ ref:
   Ref(id=192903209792046592, collection=Ref(id=posts, collection=Ref(id=collections))),
  ts: 1527088583491065,
  data: { title: 'My cat and other marvels' } }

The match function returns a logical set of elements, which can be combined with other sets with set-operations like join, intersect, subtract, etc.

Update posts

You can easily modify documents by supplying the new data along with the reference to the document. For example, we want to add tags to each of our blog posts:

client.query(
  q.Update(
    q.Ref(q.Collection('posts'), '192903209792046592'),
    { data: { tags: ['pet', 'cute'] } },
  )
)
.then((ret) => console.log(ret))
{ ref:
   Ref(id=192903209792046592, collection=Ref(id=posts, collection=Ref(id=collections))),
  ts: 1527350534706104,
  data:
   { title: 'My cat and other marvels', tags: [ 'pet', 'cute' ] } }

The Update function updates specific fields in a document. It preserves the old fields if they are not specified in params. In the case of nested values (known as objects, due to the JSON data format), the old and the new values are merged. If null is specified as a value for a field, it is removed.

Replace posts

The Replace function replaces the document’s data with the fields provided in params. Old fields not mentioned in params are removed.

client.query(
  q.Replace(
    q.Ref(q.Collection('posts'), '192903209792046592'),
    { data: { title: 'My dog and other marvels' } },
  )
)
.then((ret) => console.log(ret))
{ ref:
   Ref(id=192903209792046592, collection=Ref(id=posts, collection=Ref(id=collections))),
  ts: 1527350638301882,
  data: { title: 'My dog and other marvels' } }

Note that the title has been updated, but tags has been deleted.

Delete a post

Lastly, a post can be removed using the Delete function:

client.query(
  q.Delete(
    q.Ref(q.Collection('posts'), '192903209792045568')
  )
)
.then((ret) => console.log(ret))
{ ref:
   Ref(id=192903209792045568, collection=Ref(id=posts, collection=Ref(id=collections))),
  ts: 1527349510087794,
  data: { title: 'Deep meanings in a latte' } }
client.query(
  q.Get(q.Ref(q.Collection('posts'), '192903209792045568'))
)
.then((ret) => console.log(ret))
.catch((ret) => console.log(ret))
[NotFound: document not found]

Was this article helpful?

We're sorry to hear that.
Tell us how we can improve! documentation@fauna.com

Thank you for your feedback!