Quick start

Fauna is a distributed document-relational database service. You can use Fauna databases to build secure, scalable multi-tenant applications.

In this quick start guide, you’ll:

  • Create a Fauna database with demo data.

  • Run Fauna Query Language (FQL) queries to read and write data.

  • Build an application that integrates with Fauna using a client driver.

For a high-level overview of Fauna and its features, check out the Docs home page and the Overview.

Prefer to learn using a sample application? Check out the Fauna JavaScript sample app on GitHub.

Create a database

Create a database with demo data:

  1. Log in to the Fauna Dashboard. You can sign up for a free account at https://dashboard.fauna.com/register.

  2. Create a database. When creating the database, enable demo data.

  3. Navigate to the database in the Dashboard.

Read data

You use FQL queries to read data in a Fauna database. You can run FQL queries in the Dashboard Shell.

Get all collection documents

Fauna stores data as JSON-like documents in collections.

You can filter and fetch documents from a collection as a set, and iterate through each document.

Run the following FQL query to get all documents in the demo data’s Customer collection. The query calls the collection.all() method on the Customer collection:

// Calls `all()` on the `Customer` collection.
Customer.all()

The query returns a set of Customer collection documents. Each document has a 64-bit id that’s unique to the document within the collection.

{
  data: [
    {
      // Document ID, generated on write.
      id: "111",
      coll: Customer,
      ts: Time("2099-07-29T21:46:41.440Z"),
      cart: Order("<ORDER_DOCUMENT_ID>"),
      orders: "hdW...",
      name: "Alice Appleseed",
      email: "alice.appleseed@example.com",
      address: {
        street: "87856 Mendota Court",
        city: "Washington",
        state: "District of Columbia",
        postalCode: "20220",
        country: "US"
      }
    },
    ...
  ]
}

For one-off queries, use collection.where() to filter documents using a predicate:

// Runs an unindexed query.
Customer.where(.email == "alice.appleseed@example.com")

For better performance on large datasets, use an index with a term to run an exact match search. An index stores, or covers, specific document field values for quick retrieval.

You define indexes in an FSL collection schema. The demo data includes a schema for the Customer collection:

collection Customer {
  ...

  // Unique constraint.
  // Ensures a field value or combination of field values
  // is unique for each document in the collection.
  unique [.email]

  // Definition for the `byEmail()` index.
  // Use indexes to filter and sort documents
  // in a performant way.
  index byEmail {
    // `terms` are document fields for exact match searches.
    // In this example, you get `Customer` collection documents
    // by their unique `email` field value.
    terms [.email]
  }
}

You call an index as a method on its collection:

// Uses the `Customer` collection's `byEmail()` index
// to run an exact match search on an `email` field value.
Customer.byEmail("alice.appleseed@example.com")
{
  data: [
    {
      id: "111",
      coll: Customer,
      ts: Time("2099-07-29T21:46:41.440Z"),
      cart: Order("<ORDER_DOCUMENT_ID>"),
      orders: "hdW...",
      name: "Alice Appleseed",
      email: "alice.appleseed@example.com",
      address: {
        street: "87856 Mendota Court",
        city: "Washington",
        state: "District of Columbia",
        postalCode: "20220",
        country: "US"
      }
    }
  ]
}

Sort data

You can use method chaining to call a method on the output of another method. Expressions are evaluated sequentially from left to right.

The following query calls set.order() on the output of collection.all():

// Runs an unindexed query.
// Sorts `Product` collection documents by:
// - `price` (ascending), then ...
// - `name` (ascending), then ...
// - `description` (ascending), then ...
// - `stock` (ascending).
Product.all().order(.price, .name, .description, .stock)

For better performance on large datasets, use an index with values to sort collection documents.

The demo data includes a schema for the Product collection:

collection Product {
  ...
  // Defines the `sortedByPriceLowToHigh()` index.
  index sortedByPriceLowToHigh {
    // `values` are document fields for sorting and range searches.
    values [.price, .name, .description, .stock]
  }
}

Call the index in a query:

// Uses the `Product` collection's `sortedByPriceLowToHigh()` index
// to sort `Product` collection documents by:
// - `price` (ascending), then ...
// - `name` (ascending), then ...
// - `description` (ascending), then ...
// - `stock` (ascending).
Product.sortedByPriceLowToHigh()

For one-off queries, use Collection.where() to filter documents:

// Runs an unindexed query.
// Get `Product` collection documents with a `price` between
// 10_00  and 100_00 (inclusive).
Product.where(.price >= 10_00 && .price <= 100_00)
  .order(.price, .name, .description, .stock)

For better performance on large datasets, use an index with values to run range searches on collection documents:

// Get `Product` collection documents with a `price` between
// 10_00  and 100_00 (inclusive).
Product.sortedByPriceLowToHigh({ from: 10_00, to: 100_00 })

Project results

Projection lets you return only the specific fields you want from queries. Use projection to create smaller response payloads and decrease egress costs.

// Get only product names, descriptions, and prices
Product.sortedByPriceLowToHigh() {
  name,
  description,
  price
}
{
  data: [
    {
      name: "single lime",
      description: "Conventional, 1 ct",
      price: 35
    },
    ...
    {
      name: "donkey pinata",
      description: "Original Classic Donkey Pinata",
      price: 2499
    }
  ]
}

Paginate results

Fauna automatically paginates sets with 16 or more elements. Use set.pageSize() to change the maximum number of elements per page:

// Calls `pageSize()` with a size of `2`.
Product.sortedByPriceLowToHigh().pageSize(2) {
  name,
  description,
  price
}

If a set is paginated and a subsequent page is available, the result includes an after cursor:

{
  // The result set contains two elements or fewer.
  data: [
    {
      name: "single lime",
      description: "Conventional, 1 ct",
      price: 35
    },
    {
      name: "cilantro",
      description: "Organic, 1 bunch",
      price: 149
    }
  ],
   // Use the `after` cursor to get the next page of results.
  after: "hdW..."
}

To get the next page of results, pass the after cursor to Set.paginate():

Set.paginate("hdW...")

The Fauna client drivers also include methods for automatically iterating through pages. See:

Write data

You also use FQL queries to write data to a Fauna database.

Create a document

Use collection.create() to create, or insert, a document in a collection:

// Calls `create()` on the `Customer` collection.
// Creates a `Customer` collection document.
Customer.create({
  name: "John Doe",
  email: "jdoe@example.com",
  address: {
    street: "87856 Mendota Court",
    city: "Washington",
    state: "District of Columbia",
    postalCode: "20220",
    country: "US"
  }
})

If wanted, you can also provide a document id. If you don’t provide an ID, Fauna auto-generates one. Once assigned, the document ID is immutable and can’t be changed.

Customer.create({
  id: "12345",
  name: "Jacob Doe",
  email: "jacob.doe@example.com",
  address: {
    street: "87856 Mendota Court",
    city: "Washington",
    state: "District of Columbia",
    postalCode: "20220",
    country: "US"
  }
})

Update a document

Use document.update() to update a document:

// Declares a `customer` variable.
// Uses the `Customer` collection's `byEmail()` index to
// get `Customer` collection documents by `email` field value.
// In the `Customer` collection, `email` field values are unique
// so return the `first()` (and only) document.
let customer = Customer.byEmail("jdoe@example.com").first()

// Calls `update()` on the `Customer` collection document.
// `?.` safely calls `update()` only if `customer` contains
// a non-null value.
customer?.update({
  // Updates the existing `name` field value.
  name: "Jonathan Doe"
})

Replace a document

Use document.replace() to replace a document:

let customer = Customer.byEmail("jdoe@example.com").first()

// Calls `replace()` on the `Customer` collection document.
customer?.replace({
  name: "Jane Doe",
  email: "jdoe@example.com",
  address: {
    street: "87856 Mendota Court",
    city: "Washington",
    state: "District of Columbia",
    postalCode: "20220",
    country: "US"
  }
})

Delete a document

Use document.delete() to delete a document:

let customer = Customer.byEmail("jdoe@example.com").first()

// Calls `delete()` on the `Customer` collection document.
customer?.delete()

The query returns a NullDoc, which indicates the document no longer exists:

Customer("<DOCUMENT_ID>") /* deleted */

Bulk writes

Use set.forEach() to iteratively update each document in a set:

// Get a set of `Customer` collection documents with an
// `address` in the `state` of `DC`.
let customers = Customer.where( .address?.state == "DC" )
// Use `forEach()` to update each document in the previous set.
customers.forEach(doc => doc.update({
  address: {
    street: doc?.address?.street,
    city: doc?.address?.city,
    state: "District of Columbia",
    postalCode: doc?.address?.postalCode,
    country: doc?.address?.country,
  }
})) // `forEach()` returns `null`.

For more examples, see Bulk writes.

Document relationships

A document relationship establishes associations between documents. Fauna supports one-to-one, one-to-many, and many-to-many relationships between documents in different collections.

Create a relationship between documents

To create a document relationship, include the document as a field value:

// Uses the `Category` collection's `byName()`
// index to get the first category named `produce`.
let produce = Category.byName("produce").first()

// Creates a `Product` collection document.
Product.create({
  name: "lemons",
  description: "Organic, 16 ct",
  price: 2_49,
  stock: 200,
  // Adds the previous `Category` collection document as
  // a `category` field value.
  category: produce
})

Traverse document relationships

You can project a field that contains a document to dynamically traverse the document relationship:

// Get product names, category, and prices
Product.sortedByCategory() {
  name,
  category,
  price
}
{
  data: [
    {
      name: "cups",
      // Traverses the `Category` collection document in
      // the `category` field.
      category: {
        id: "<CATEGORY_DOCUMENT_ID>",
        coll: Category,
        ts: Time("2099-07-30T12:35:24.630Z"),
        products: "hdW...",
        name: "party",
        description: "Party Supplies"
      },
      price: 698
    },
    {
      name: "donkey pinata",
      category: {
        id: "<CATEGORY_DOCUMENT_ID>",
        coll: Category,
        ts: Time("2099-07-30T12:35:24.630Z"),
        products: "hdW...",
        name: "party",
        description: "Party Supplies"
      },
      price: 2499
    },
    ...
  ]
}

Create a child database for multi-tenancy

You can use FQL to programmatically create child databases. Child databases are represented as Database collection documents in the parent database:

// Create a child database named `childDB`
Database.create({
  name: "childDB",
  typechecked: true,
  priority: 10
})

For multi-tenant applications, you can create a child database for each tenant. Each database is isolated from its peers and becomes available immediately upon creation.

You can populate and access the database’s data using a secret that’s scoped to the database.

You can also use a scoped key to access a child database from its parent and manage its data.

Create a secret

To connect to Fauna using a client you need a secret to authenticate with Fauna. Each secret is scoped to a specific database.

Fauna supports several secret types. For the quick start, create a key, which is a type of secret:

  1. In the upper left pane of Dashboard’s Explorer page, click the demo database, and click the Keys tab.

  2. Click Create Key.

  3. Choose a Role of Server.

  4. Click Save.

  5. Copy the Key Secret. The secret is scoped to the database.

Build an application

Next, create a basic application that integrates with Fauna using a Fauna client driver.

A client driver sends FQL queries and receives responses using your preferred programming language. The client deserializes query responses into the language’s corresponding data types.

JavaScriptPythonGoC#Java

 

  1. Set the FAUNA_SECRET environment variable to your key’s secret. Fauna’s client drivers can access the secret from this variable.

    export FAUNA_SECRET=<KEY_SECRET>
  2. Create a directory for the app and install the driver.

    mkdir app
    cd app
    go mod init app
    go get github.com/fauna/fauna-go
    mkdir app
    cd app
    npm install fauna
    mkdir app
    cd app
    pip install fauna

    The C# client driver is currently in beta.

    Navigate to your .NET or C# project directory and add the Fauna package to your project.

    mkdir app
    cd app
    dotnet new console
    dotnet add package Fauna --prerelease

    The JVM client driver is currently in alpha.

    For the quick start, install the driver using Gradle.

    Navigate to your Java project directory and create a project.

    mkdir app
    cd app
    gradle init --type java-application

    When prompted, enter:

    • Enter target Java version: 17

    • Project name: app

    • Select application structure: 1 (Single application project)

    • Select build script DSL: 2 (Groovy)

    • Select test framework: 1 (JUnit 4)

    • Generate build using new APIs…​: no

    Edit app/build.gradle and add the following dependency:

    dependencies {
        ...
        implementation "com.fauna:fauna-jvm:0.1.0-M4"
        ...
    }

    Save app/build.gradle.

  3. Create an app.go file and add the following code:

    Create an app.mjs file and add the following code:

    Create an app.py file and add the following code:

    Edit the Program.cs file and replace the code with the following:

    Edit the app/src/main/java/org/example/App.java file and replace the code with the following:

    import { Client, fql } from "fauna";
    
    const client = new Client();
    
    // Build a query using the `fql` function
    const query = fql`
      Product.sortedByPriceLowToHigh().pageSize(2) {
        name,
        description,
        price
      }`;
    
    // Run the query
    const pages = client.paginate(query);
    const products = [];
    for await (const product of pages.flatten()) {
      products.push(product)
    }
    console.log(products)
    client.close();
    from fauna import fql
    from fauna.client import Client
    from fauna.errors import FaunaException
    
    client = Client()
    
    try:
      # Build a query using the `fql` function
      query = fql("""
        Product.sortedByPriceLowToHigh().pageSize(2) {
          name,
          description,
          price
      }""")
    
      # Run the query
      pages = client.paginate(query);
    
      products = []
      for product in pages.flatten():
          products.append(product)
      print(products)
    except FaunaException as e:
        print(e)
    finally:
        client.close();
    using Fauna;
    using Fauna.Exceptions;
    using static Fauna.Query;
    using System.Text.Json;
    
    try
    {
        var secret = Environment.GetEnvironmentVariable("FAUNA_SECRET");
        if (string.IsNullOrEmpty(secret))
        {
            Console.WriteLine("Fauna secret is not valid");
            return;
        }
    
        var cfg = new Configuration(secret);
        // Initialize the client to connect to Fauna
        var client = new Client(cfg);
    
        // Compose a query
        var query = FQL($@"Product.sortedByPriceLowToHigh().pageSize(2) {{
          name,
          description,
          price
        }}");
    
        // Run the query
        // PaginateAsync returns an IAsyncEnumerable of pages
        var response = client.PaginateAsync<Dictionary<string, object?>>(query);
    
        var allProducts = new List<Dictionary<string, object?>>();
    
        await foreach (var page in response)
        {
            allProducts.AddRange(page.Data);
        }
    
        // Serialize the list of products to JSON
        var jsonOptions = new JsonSerializerOptions { WriteIndented = true };
        string jsonString = JsonSerializer.Serialize(allProducts, jsonOptions);
    
        // Print the JSON array
        Console.WriteLine(jsonString);
    }
    catch (FaunaException e)
    {
        Console.WriteLine(e);
    }
    using Fauna;
    using Fauna.Exceptions;
    using static Fauna.Query;
    using System.Text.Json;
    
    try
    {
        var secret = Environment.GetEnvironmentVariable("FAUNA_SECRET");
        if (string.IsNullOrEmpty(secret))
        {
            Console.WriteLine("Fauna secret is not valid");
            return;
        }
    
        var cfg = new Configuration(secret);
        var client = new Client(cfg);
    
        // Build a query using the `FQL` function
        var query = FQL($@"Product.sortedByPriceLowToHigh().pageSize(2) {{
          name,
          description,
          price
        }}");
    
        // Run the query
        var response = client.PaginateAsync<Dictionary<string, object?>>(query);
    
        var allProducts = new List<Dictionary<string, object?>>();
    
        await foreach (var page in response)
        {
            allProducts.AddRange(page.Data);
        }
    
        var jsonOptions = new JsonSerializerOptions { WriteIndented = true };
        string jsonString = JsonSerializer.Serialize(allProducts, jsonOptions);
    
        Console.WriteLine(jsonString);
    }
    catch (FaunaException e)
    {
        Console.WriteLine(e);
    }
    package org.example;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    
    import com.fauna.annotation.FaunaField;
    import com.fauna.annotation.FaunaObject;
    import com.fauna.client.Fauna;
    import com.fauna.client.FaunaClient;
    import com.fauna.exception.FaunaException;
    import com.fauna.query.builder.Query;
    import com.fauna.response.QuerySuccess;
    import com.fauna.serialization.generic.PageOf;
    import com.fauna.types.Page;
    
    public class App {
    
        // Define class for `Product` documents
        // in expected results.
        @FaunaObject
        public static class Product {
    
            @FaunaField(name = "name")
            public String name;
    
            @FaunaField(name = "description")
            public String description;
    
            @FaunaField(name = "price")
            public double price;
        }
    
        public static void main(String[] args) {
            try {
                // Initialize the client.
                FaunaClient client = Fauna.client();
    
                // Build a query using an `fql` template.
                Query query = Query.fql("""
                    Product.sortedByPriceLowToHigh().pageSize(2) {
                        name,
                        description,
                        price
                    }
                """);
    
                CompletableFuture<QuerySuccess<Page<Product>>> futureResult = client.asyncQuery(query, new PageOf<>(Product.class));
                QuerySuccess<Page<Product>> result = futureResult.get();
                printResults(result.getData());
    
            } catch (FaunaException e) {
                System.err.println("Fauna error occurred: " + e.getMessage());
                e.printStackTrace();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    
        private static void printResults(Page<Product> page) {
            for (Product product : page.data()) {
                System.out.println("Name: " + product.name);
                System.out.println("Description: " + product.description);
                System.out.println("Price: " + product.price);
                System.out.println("--------");
            }
            System.out.println("After: " + page.after());
        }
    }

    The application prints product data from the demo database.

  4. Run the application:

    go run app.go
    node app.mjs
    python app.py
    dotnet run
    ./gradlew run

    The application prints the following:

    [
      {
        name: 'single lime',
        description: 'Conventional, 1 ct',
        price: 0.35
      },
      {
        name: 'cilantro',
        description: 'Organic, 1 bunch',
        price: 1.49
      }
    ]
    [
      {
        "name": "single lime",
        "description": "Conventional, 1 ct",
        "price": 0.35
      },
      {
        "name": "cilantro",
        "description": "Organic, 1 bunch",
        "price": 1.49
      }
    ]
    [
      {
        name: 'single lime',
        description: 'Conventional, 1 ct',
        price: 0.35
      },
      {
        name: 'cilantro',
        description: 'Organic, 1 bunch',
        price: 1.49
      }
    ]
    [
      {
        "name": "single lime",
        "description": "Conventional, 1 ct",
        "price": 0.35
      },
      {
        "name": "cilantro",
        "description": "Organic, 1 bunch",
        "price": 1.49
      }
    ]
    Name: single lime
    Description: Conventional, 1 ct
    Price: 0.35
    --------
    Name: cilantro
    Description: Organic, 1 bunch
    Price: 1.49
    --------
    After: hdWC...

Next steps

Congratulations! You’ve created a database, run some queries, and integrated Fauna with an application.

As a next step, try one of the following:

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!