Python client driver for Fauna

Version: 2.0.0 Repository: fauna/fauna-python

Fauna’s Python client driver lets you run FQL queries from Python 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 Python versions

  • Python 3.9

  • Python 3.10

  • Python 3.11

  • Python 3.12

Supported cloud runtimes

  • AWS Lambda

  • Vercel Functions

Installation

The driver is available on PyPI. To install it, run:

pip install fauna

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()

from fauna import fql
from fauna.client import Client
from fauna.encoding import QuerySuccess
from fauna.errors import FaunaException

# Initialize the client to connect to Fauna
client = Client(secret='YOUR_FAUNA_SECRET')

try:
    # Compose a query
    query = fql(
        """
        Product.sortedByPriceLowToHigh() {
            name,
            description,
            price
            }"""
    )

    # Run the query
    res: QuerySuccess = client.query(query)
    print(res.data)
except FaunaException as e:
    print(e)
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:

client = 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():

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

The driver supports queries with Python primitives, lists, and dicts.

Use ${} to pass native Python variables to fql queries as kwargs. You can escape a variable by prepending an additional $.

# Create a native Python var
collection_name = 'Product'

# Pass the var to an FQL query
query = fql('''
  let collection = Collection(${collection_name})
  collection.sortedByPriceLowToHigh()''',
  collection_name=collection_name)

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
def get_product(name):
  return fql(
    'Product.byName(${name}).first()',
    name=name)

# Use the subquery in another FQL query
query = fql('''
  let product = ${get_product}
  product?.update({
    name: "pizza pie"
  })''',
  get_product=get_product('pizza'))

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.
query = fql('''
  Product.sortedByPriceLowToHigh()
    .pageSize(2)''')

pages = client.paginate(query);

for products in pages:
    for product in products:
        print(products)

Query statistics

Successful query responses and ServiceError errors return query statistics:

from fauna import fql
from fauna.client import Client
from fauna.errors import ServiceError

client = Client(secret='YOUR_FAUNA_SECRET')

try:
    query = fql('"Hello world"')
    res = client.query(query)
    print(res.stats)
except ServiceError as e:
    if e.stats is not None:
        print(e.stats)
    # more error handling...

User-defined classes

Serialization and deserialization with user-defined classes is not supported.

When composing FQL queries, adapt your classes into dicts or lists. When instantiating classes from a query result, build them from the expected result.

class MyClass:
    def __init__ (self, my_prop):
        self.my_prop = my_prop

    def to_dict(self):
        return { 'my_prop': self.my_prop }

    @static_method
    def from_result(obj):
        return MyClass(obj['my_prop'])

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.

from datetime import timedelta
from fauna.client import Client
from fauna.client.headers import Header
from fauna.client.endpoints import Endpoints

config = {
    # Configure the client
    'secret': 'YOUR_FAUNA_SECRET',
    'endpoint': Endpoints.Default,
    'client_buffer_timeout': timedelta(seconds=5),
    'http_read_timeout': None,
    'http_write_timeout': timedelta(seconds=5),
    'http_connect_timeout': timedelta(seconds=5),
    'http_pool_timeout': timedelta(seconds=5),
    'http_idle_timeout': timedelta(seconds=5),
    'max_attempts': 3,
    'max_backoff': 20,

    # Set default query options
    'additional_headers': {'foo': 'bar'},
    'linearized': False,
    'max_contention_retries': 5,
    'query_tags': {'name': 'hello world query'},
    'query_timeout': timedelta(seconds=60),
    'typecheck': True,
}

client = Client(**config)

The following table outlines supported parameters for the Client() constructor and their defaults.

Parameter Type Required Description

secret

string

Yes

Fauna key, access token, or JWT used to authorize requests. Defaults to the FAUNA_SECRET environment variable.

endpoint

string

URL for the Fauna endpoint. Defaults to the FAUNA_ENDPOINT environment variable. If FAUNA_ENDPOINT is not set, defaults to Endpoints.Default (https://db.fauna.com).

client_buffer_timeout

datetime.timedelta

Additional time beyond the query timeout for the driver to abort if no response is received. Used to account for network latency. Defaults to DefaultClientBufferTimeout (5 seconds).

If a client timeout occurs, the driver throws an instance of NetworkError.

http_read_timeout

datetime.timedelta

Maximum time the driver waits to receive data, such as a chuck of the response body, from Fauna after sending a request. Defaults to DefaultHttpReadTimeout (None).

If a read timeout occurs, the driver throws an instance of ReadTimeout.

http_write_timeout

datetime.timedelta

Maximum time the driver waits to send to Fauna over the network. Defaults to DefaultHttpWriteTimeout (5 seconds).

If a write timeout occurs, the driver throws an instance of WriteTimeout.

http_connect_timeout

datetime.timedelta

Maximum time to wait for the connection to Fauna to complete. Defaults to DefaultHttpConnectTimeout (5 seconds).

If a connection timeout occurs, the driver throws an instance of ConnectTimeout.

http_pool_timeout

datetime.timedelta

Maximum time the driver waits to get a connection from the connection pool. Defaults to DefaultHttpPoolTimeout (5 seconds).

A timeout may occur if 20 connections are currently in use and one isn’t released before the timeout. If a connection pool timeout occurs, the driver throws an instance of PoolTimeout.

http_idle_timeout

datetime.timedelta

Maximum amount of time an idle (keep-alive) connection remains open before closing. Defaults to DefaultIdleConnectionTimeout (5 seconds).

Do not set http_idle_timeout to a small value. Instead, call Client.close() in your application code after all requests complete.

max_attempts

int

Maximum number of retry attempts for a query. Defaults to 3, including the initial attempt.

To disable retries, set max_attempts to 1 or less.

max_backoff

int

Maximum time, in seconds, to wait before retrying a query if the request returns a 429 HTTP status code. Defaults to 20 seconds.

additional_headers

Dict[str, str]

Additional HTTP headers to include in requests. Defaults to None.

linearized

bool

If true, queries from the client are linearized, ensuring strict serialization of reads and writes. Defaults to false.

max_contention_retries

int

Maximum number of times Fauna retries a query after a contention error. These are server-side retries. Defaults to None (no retries).

query_tags

Mapping[str, str]

Key-value tags used to identify the query. Defaults to None.

Query tags are included in query logs and the response body for successful queries. The tags are typically used for monitoring.

query_timeout

datetime.timedelta

Maximum amount of time Fauna runs a query before marking it as failed. Defaults to DefaultQueryTimeout (5 seconds).

If a query timeout occurs, the driver throws an instance of QueryTimeoutError.

typecheck

bool

If true, enables type checking for queries. Defaults to the database’s type checking setting.

If true, type checking must be enabled on the database.

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:

client = Client()

Retries

By default, the client automatically retries a query if the request returns a 429 HTTP status code. Retries use an exponential backoff.

Use the Client configuration's max_backoff parameter to set the maximum time between retries. Similarly, use max_attempts to set the maximum number of retry attempts.

Query options

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

  • Client.query()

  • Client.paginate()

You can pass an QueryOptions argument to override these defaults:

options = QueryOptions(
    additional_headers={'foo': 'bar'},
    linearized=False,
    max_contention_retries=5,
    query_tags={'name': 'hello world query'},
    query_timeout=timedelta(seconds=60),
    traceparent='00-750efa5fb6a131eb2cf4db39f28366cb-000000000000000b-00',
    typecheck=True
)

client.query(fql('"Hello world"'), options)

The following table outlines supported properties for the QueryOptions class.

Property Type Required Description

additional_headers

Dict[str, str]

Additional HTTP headers to include in the request.

linearized

bool

If true, the query is linearized, ensuring strict serialization of reads and writes.

max_contention_retries

int

Maximum number of times Fauna retries the query after a contention error. These are server-side retries.

query_tags

Mapping[str, str]

Key-value tags used to identify the query.

Query tags are included in query logs and the response body for successful queries. The tags are typically used for monitoring.

query_timeout

datetime.timedelta

Maximum amount of time Fauna runs the query before marking it as failed.

If a query timeout occurs, the driver throws an instance of QueryTimeoutError.

traceparent

str

W3C-compliant traceparent ID for the request. If you provide an invalid traceparent ID, Fauna generates a valid one. Defaults to None.

The traceparent ID is included in query logs. Traceparent IDs are typically used for monitoring.

typecheck

bool

If true, enables type checking for the query.

If true, type checking must be enabled on the database.

Event Streaming

The driver supports Event Streaming.

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():

import fauna

from fauna import fql
from fauna.client import Client, StreamOptions

client = Client(secret='YOUR_FAUNA_SECRET')

response = client.query(fql('''
  let set = Product.all()
  {
    initialPage: set.pageSize(10),
    streamToken: set.toStream()
  }
  '''))

initialPage = response.data['initialPage']
streamToken = response.data['streamToken']

client.stream(streamToken)

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

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

client.stream(query)

Iterate on a stream

Client.stream() returns an iterator that emits events as they occur. You can use a generator expression to iterate through the events:

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

with client.stream(query) as stream:
    for event in stream:
        eventType = event['type']
        if (eventType == 'add'):
            print('Add event: ', event)
            ## ...
        elif (eventType == 'update'):
            print('Update event: ', event)
            ## ...
        elif (eventType == 'remove'):
            print('Remove event: ', event)
            ## ...

Close a stream

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

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

count = 0
with client.stream(query) as stream:
    for event in stream:
        print('Stream event', event)
        # ...
        count+=1

        if (count == 2):
            stream.close()

Error handling

If a non-retryable error occurs when opening or processing a stream, Fauna raises a FaunaException:

import fauna

from fauna import fql
from fauna.client import Client
from fauna.errors import FaunaException

client = Client(secret='YOUR_FAUNA_SECRET')

try:
    with client.stream(fql(
        'Product.all().changesOn(.price, .quantity)'
    )) as stream:
        for event in stream:
            print(event)
        # ...
except FaunaException as e:
    print('error ocurred with stream: ', e)

Stream options

The Client configuration sets default options for the Client.stream() method.

You can pass a StreamOptions object to override these defaults:

options = StreamOptions(
    max_attempts=5,
    max_backoff=1,
    start_ts=1710968002310000,
    status_events=True
)

client.stream(fql('Product.all().toStream()'), options)

The following table outlines supported properties for the StreamOptions class.

Property Type Required Description

max_attempts

int

Maximum number of retry attempts for a query.

max_backoff

int

Maximum time, in seconds, to wait before retrying a query if the request returns a 429 HTTP status code.

start_ts

int

Stream start time in microseconds since the Unix epoch. Used to reconnect a disconnected stream. start_ts is typically the time the stream disconnected.

start_ts must be later than the creation time of the stream token.

The period between the stream start and the start_ts can’t exceed the history_days value for source set’s collection. If a collection’s history_days is 0 or unset, the period can’t exceed 15 minutes.

status_events

bool

If True, the stream includes periodic status events. These events are sent when a stream starts or reconnects. They’re also periodically sent to:

  • Keep the client connection open.

  • Send stats on operations consumed by event processing, including discarded events that aren’t sent.

Defaults to False.

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!