JavaScript client driver for Fauna

Version: 1.3.1 Repository: fauna/fauna-js

Fauna’s JavaScript client driver lets you run FQL queries from JavaScript or TypeScript applications.

This guide shows how to set up the driver and use it to run FQL queries. The examples use Fauna’s demo data.

This driver can only be used with FQL v10. It’s not compatible with earlier versions of FQL. To use earlier FQL versions, use the faunadb package.

Supported runtimes

The driver supports the following runtime environments.

Server-side

  • Current - v20

  • LTS - v18

Cloud providers

  • Cloudflare Workers

  • AWS Lambda

  • Netlify

  • Vercel

Browsers

Stable versions of:

  • Chrome 69+

  • Firefox 62+

  • Safari 12.1+

  • Edge 79+

Installation

The driver is available on npm. Install it using your preferred package manager:

npm install fauna

Browsers can import the driver using a CDN link:

<script type="module">
  import * as fauna from "https://cdn.jsdelivr.net/npm/fauna@latest/dist/browser/index.js";
</script>

Basic usage

The following application:

  • Initializes a client instance to connect to Fauna

  • Composes a basic FQL query using an fql template

  • Runs the query using Client.query()

import { Client, fql, FaunaError } from "fauna";
// Use `require` for CommonJS:
// const { Client, fql, FaunaError } = require('fauna');

// Initialize the client to connect to Fauna
const client = new Client({
    secret: 'YOUR_FAUNA_SECRET'
});

try {
  // Compose a query
  const query = fql`
    Product.sortedByPriceLowToHigh() {
      name,
      description,
      price
    }`;

  // Run the query
  const response = await client.query(query);
  console.log(response.data);

} catch (error) {
  if (error instanceof FaunaError) {
    console.log(error);
  }
} finally {
  // Clean up any remaining resources
  client.close();
}

Connect to Fauna

To connect to Fauna, initialize a Client instance using a Fauna key, access token, or JWT:

const client = new Client({
  secret: 'YOUR_FAUNA_SECRET' // Your key, access token, or JWT
});

If not specified, secret defaults to the FAUNA_SECRET environment variable. For other configuration options, see Client configuration.

Multiple connections

You can use a single client instance to run multiple asynchronous queries at once. The driver manages HTTP connections as needed. Your app doesn’t need to implement connection pools or other connection management strategies.

You can create multiple client instances to connect to Fauna using different credentials or client configurations.

Run FQL queries

Use fql templates to compose FQL queries. Run the queries using Client.query():

const query = fql`Product.sortedByPriceLowToHigh()`;
client.query(query)

By default, Client.query() uses query options from the Client configuration. You can pass options to Client.query() to override these defaults. See Query options.

Variable interpolation

Use ${} to pass native JavaScript variables to fql queries:

// Create a native JS var
const collectionName = "Product";

// Pass the var to an FQL query
const query = fql`
  let collection = Collection(${collectionName})
  collection.sortedByPriceLowToHigh()`;

client.query(query);

Passed variables are encoded to an appropriate type and passed to Fauna’s HTTP API. This helps prevent injection attacks.

Subqueries

You can use native variables to pass an FQL query to another FQL query. This lets you create reusable subqueries:

// Create a reusable FQL subquery
const getProduct = name => {
  return fql`Product.byName(${name}).first()`
}

// Use the subquery in another FQL query
const query = fql`
  let product = ${getProduct("pizza")}
  product?.update({
    name: "pizza pie"
  })`;

client.query(query);

Pagination

Use Client.paginate() to iterate a set that contains more than one page of results. Client.paginate() accepts the same Query options as Client.query().

// Adjust `pageSize()` size as needed.
const query = fql`
  Product.sortedByPriceLowToHigh()
    .pageSize(2)`;

const pages = client.paginate(query);

for await (const products of pages) {
  for (const product of products) {
    console.log(product)
    // ...
  }
}

Query statistics

Successful query responses and ServiceError errors include query statistics:

try {
  const response = await client.query(fql`"Hello world"`);
  console.log(response.stats);

} catch (error) {
  if (error instanceof ServiceError) {
    const info = error.queryInfo;
    const stats = info.stats;
  }
}

Output:

{
  compute_ops: 1,
  read_ops: 0,
  write_ops: 0,
  query_time_ms: 0,
  contention_retries: 0,
  storage_bytes_read: 0,
  storage_bytes_write: 0,
  rate_limits_hit: [],
  attempts: 1
}

TypeScript support

The driver supports TypeScript. For example, you can apply a type parameter to your FQL query results:

import { fql, Client, type QuerySuccess } from "fauna";

const client = new Client();

type User = {
  name: string;
  email: string;
};

const query = fql`{
  name: "Alice",
  email: "alice@site.example",
}`;

const response: QuerySuccess<User> = await client.query<User>(query);
const user_doc: User = response.data;

console.assert(user_doc.name === "Alice");
console.assert(user_doc.email === "alice@site.example");

.d.ts files for Fauna type definitions are available in the fauna package’s /dist folder.

Client configuration

The Client instance comes with reasonable configuration defaults. We recommend using the defaults in most cases.

If needed, you can configure the client to override the defaults. This also lets you set default Query options.

import { Client, endpoints } from "fauna";

const config = {
  // Configure the client
  client_timeout_buffer_ms: 5000,
  endpoint: endpoints.default,
  fetch_keepalive: false,
  http2_max_streams: 100,
  http2_session_idle_ms: 5000,
  secret: YOUR_FAUNA_SECRET,

  // Set default query options
  format: "tagged",
  linearized: false,
  long_type: "number",
  max_attempts: 3,
  max_backoff: 20,
  max_contention_retries: 5,
  query_tags: { name: "hello world query" },
  query_timeout_ms: 60_000,
  traceparent: "00-750efa5fb6a131eb2cf4db39f28366cb-000000000000000b-00",
  typecheck: true,
};

const client = new Client(config);

Environment variables

By default, secret and endpoint default to the respective FAUNA_SECRET and FAUNA_ENDPOINT environment variables.

For example, if you set the following environment variables:

export FAUNA_SECRET=YOUR_FAUNA_SECRET
export FAUNA_ENDPOINT=https://db.fauna.com/

You can initialize the client with a default configuration:

const client = new Client();

Retry

Max attempts

The maximum number of times a query will be attempted if a retryable exception is thrown (ThrottlingError). Defaults to 3, inclusive of the initial call. The retry strategy implemented is a simple exponential backoff.

To disable retries, pass a max_attempts argument of 1 or less.

Max backoff

The maximum backoff in seconds to be observed between each retry. Defaults to 20.

Timeouts

There are a few different timeout settings that can be configured; each comes with a default setting. We recommend that most applications simply stick to the defaults.

Query timeout

The query timeout is the time, in milliseconds, that Fauna will spend executing your query before aborting with a 503 Timeout error. If a query timeout occurs, the driver will throw an instance of ServiceTimeoutError.

The query timeout can be set using the ClientConfiguration.query_timeout_ms option. Defaults to 5000 (5 seconds).

const client = new Client({ query_timeout_ms: 20_000 });

The query timeout can also be set to a different value for each query using the QueryOptions.query_timeout_ms option. Doing so overrides the client configuration when performing this query.

const response = await client.query(myQuery, { query_timeout_ms: 20_000 });

Client timeout

The client timeout is the time, in milliseconds, that the client will wait for a network response before canceling the request. If a client timeout occurs, the driver will throw an instance of NetworkError.

The client timeout is always the query timeout plus an additional buffer. This ensures that the client always waits for at least as long Fauna could work on your query and account for network latency. The client timeout buffer is configured by setting the client_timeout_buffer_ms option. The default value for the buffer if you do not provide on is 5000 (5 seconds), therefore the default client timeout is 10000 (10 seconds) when considering the default query timeout.

const client = new Client({ client_timeout_buffer_ms: 6000 });

HTTP/2 session idle timeout

The HTTP/2 session idle timeout is the time, in milliseconds, that an HTTP/2 session will remain open after there is no more pending communication. Once the session idle time has elapsed the session is considered idle and the session is closed. Subsequent requests will create a new session; the session idle timeout does not result in an error.

Configure the HTTP/2 session idle timeout using the http2_session_idle_ms option. Defaults to 5000 (5 seconds).

This setting only applies to clients using HTTP/2 implementations; for example, the default client for Node.js runtimes.

const client = new Client({ http2_session_idle_ms: 6000 });

Your application process may continue executing after all requests are completed for the duration of the session idle timeout. To prevent this, it is recommended to call Client.close() once all requests are complete. It is not recommended to set http2_session_idle_ms to small values.

Setting http2_session_idle_ms to small values can lead to a race condition where requests cannot be transmitted before the session is closed, yielding ERR_HTTP2_GOAWAY_SESSION errors.

Query options

The Client configuration sets default query options for the following methods:

  • Client.query()

  • Client.paginate()

You can pass an options object to override these defaults:

const options = {
  arguments: { name: "Alice" },
  format: "tagged",
  linearized: false,
  long_type: "number",
  max_contention_retries: 5,
  query_tags: { name: "hello world query" },
  query_timeout_ms: 60_000,
  traceparent: "00-750efa5fb6a131eb2cf4db39f28366cb-000000000000000b-00",
  typecheck: true,
};

client.query(fql`"Hello, #{name}!"`, options);

Event Streaming

Event Streaming is currently available in the beta JavaScript driver.

To install the beta driver:

npm install fauna@beta

Start a stream

To get a stream token, append toStream() or changesOn() to a set from a supported source.

To start and subscribe to the stream, pass the stream token to Client.stream():

const response = await client.query(fql`
  let set = Product.all()

  {
    initialPage: set.pageSize(10),
    streamToken: set.toStream()
  }
`);
const { initialPage, streamToken } = response.data;

client.stream(streamToken)

You can also pass a query that produces a stream token directly to Client.stream():

const query = fql`Product.all().changesOn(.price, .quantity)`

client.stream(query)

Iterate on a stream

You can iterate on the stream using an async loop:

try {
  for await (const event of stream) {
    switch (event.type) {
      case "update":
      case "add":
      case "remove":
        console.log("Stream event:", event);
        // ...
        break;
    }
  }
} catch (error) {
  // An error will be handled here if Fauna returns a terminal, "error" event, or
  // if Fauna returns a non-200 response when trying to connect, or
  // if the max number of retries on network errors is reached.

  // ... handle fatal error
}

Or you can use a callback function:

stream.start(
  function onEvent(event) {
    switch (event.type) {
      case "update":
      case "add":
      case "remove":
        console.log("Stream event:", event);
        // ...
        break;
    }
  },
  function onFatalError(error) {
    // An error will be handled here if Fauna returns a terminal, "error" event, or
    // if Fauna returns a non-200 response when trying to connect, or
    // if the max number of retries on network errors is reached.

    // ... handle fatal error
  }
);

Close a stream

Use <stream>.close() to close a stream:

const stream = await client.stream(fql`Product.all().toStream()`)

let count = 0;
for await (const event of stream) {
  console.log("Stream event:", event);
  // ...
  count++;

  // Close the stream after 2 events
  if (count === 2) {
    stream.close()
    break;
  }
}

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!