Fauna v10 JavaScript client driver (current)
Version: 2.2.0 | 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.
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.
Cloud providers
-
Cloudflare Workers
-
AWS Lambda (See AWS Lambda connections)
-
Netlify
-
Vercel
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>
API reference
API reference documentation for the driver is available at https://fauna.github.io/fauna-js/.
Sample app
For a practical example, check out the JavaScript sample app.
This sample app is a production-ready e-commerce application that uses Node.js and the Fauna JavaScript driver. The source code includes comments highlighting best practices for using the driver and composing FQL queries.
Basic usage
The following application:
-
Initializes a client instance to connect to Fauna
-
Composes a basic FQL query using an
fql
string template -
Runs the query using
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: '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
Each Fauna query is an independently authenticated request to the Core HTTP API’s Query endpoint. You authenticate with Fauna using an authentication secret.
Get an authentication secret
Fauna supports several secret types. For testing, you can create a key, which is a type of secret:
-
Log in to the Fauna Dashboard.
-
In the Dashboard, create a database and navigate to it.
-
In the upper left pane of the Dashboard’s Explorer page, click the demo database, and click the Keys tab.
-
Click Create Key.
-
Choose a Role of server.
-
Click Save.
-
Copy the Key Secret. The secret is scoped to the database.
Initialize a client
To send query requests to Fauna, initialize a Client
instance using a Fauna
authentication secret:
const client = new Client({
secret: 'FAUNA_SECRET'
});
If not specified, secret
defaults to the FAUNA_SECRET
environment variable.
For other configuration options, see Client configuration.
Connect to a child database
A scoped key lets you use a parent database’s admin key to send query requests to its child databases.
For example, if you have an admin key for a parent database and want to
connect to a child database named childDB
, you can create a scoped key using
the following format:
// Scoped key that impersonates an `admin` key for
// the `childDB` child database.
fn...:childDB:admin
You can then initialize a Client
instance using the scoped key:
const client = new Client({
secret: 'fn...:childDB:admin'
});
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.
AWS Lambda connections
AWS Lambda freezes, thaws, and reuses execution environments for Lambda functions. See Lambda execution environment.
When an execution environment is thawed, Lambda only runs the function’s handler code. Objects declared outside of the handler method remain initialized from before the freeze. Lambda doesn’t re-run initialization code outside the handler.
Fauna drivers keep socket connections that can time out during long freezes,
causing ECONNRESET
errors when thawed.
To prevent timeouts, create Fauna client connections inside function handlers. Fauna drivers use lightweight HTTP connections. You can create new connections for each request while maintaining good performance.
Run FQL queries
Use fql
string templates to compose FQL queries. Run the queries using
query()
:
const query = fql`Product.sortedByPriceLowToHigh()`;
client.query(query)
By default, query()
uses query options from the
Client configuration. You can pass options to query()
to override
these defaults. See Query options.
You can only compose FQL queries using string templates.
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);
The driver encodes interpolated variables to an appropriate FQL type and uses the wire protocol to pass the query to the Core HTTP API’s Query endpoint. This helps prevent injection attacks.
Query composition
You can use variable interpolation to pass FQL string templates as query fragments to compose an FQL query:
// Create a reusable query fragment.
const product = fql`Product.byName("pizza").first()`;
// Use the fragment in another FQL query.
const query = fql`
let product = ${product}
product {
name,
price
}`;
client.query(query);
Pagination
Use paginate()
to iterate through a Set that contains more than one page of
results. paginate()
accepts the same Query options as 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)
// ...
}
}
Use flatten()
to get paginated results as a single, flat array:
const pages = client.paginate(query);
for await (const product of pages.flatten()) {
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({
secret: 'FAUNA_SECRET'
});
type Customer = {
name: string;
email: string;
};
const query = fql`{
name: "Alice Appleseed",
email: "alice.appleseed@example.com",
}`;
const response: QuerySuccess<Customer> = await client.query<Customer>(query);
const customer_doc: Customer = response.data;
console.assert(customer_doc.name === "Alice Applesee");
console.assert(customer_doc.email === "alice.appleseed@example.com");
Alternatively, you can apply a type parameter directly to your fql
statements
and Client
methods will infer your return types.
Due to backwards compatibility, if a type parameter is provided to a Client
method, the provided type will override the inferred type from your query.
const query = fql<User>`{
name: "Alice",
email: "alice@site.example",
}`;
// Response will be typed as `QuerySuccess<User>`.
const response = await client.query(query);
// `userDoc` will be automatically inferred as `User`.
const userDoc = response.data;
console.assert(userDoc.name === "Alice");
console.assert(userDoc.email === "alice@site.example");
client.close();
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: "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: { tag: "value" },
query_timeout_ms: 60_000,
traceparent: "00-750efa5fb6a131eb2cf4db39f28366cb-000000000000000b-00",
typecheck: true,
};
const client = new Client(config);
For supported properties, see ClientConfiguration in the API reference.
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=FAUNA_SECRET
export FAUNA_ENDPOINT=https://db.fauna.com/
You can initialize the client with a default configuration:
const client = new 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
property 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:
-
query()
-
paginate()
You can pass a QueryOptions
object to override these defaults:
const options = {
arguments: { name: "Alice" },
format: "tagged",
linearized: false,
long_type: "number",
max_contention_retries: 5,
query_tags: { tag: "value" },
query_timeout_ms: 60_000,
traceparent: "00-750efa5fb6a131eb2cf4db39f28366cb-000000000000000b-00",
typecheck: true,
};
client.query(fql`"Hello, #{name}!"`, options);
For supported properties, see QueryOptions in the API reference.
Event Streaming
The driver supports Event Streaming.
Start a stream
To get a stream token, append
set.toStream()
or
set.changesOn()
to a Set from a
supported
source.
To start and subscribe to the stream, pass the stream token to
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
stream()
:
const query = fql`Product.all().changesOn(.price, .stock)`
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 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;
}
}
Stream options
The Client configuration sets default options for the
stream()
method.
You can pass a StreamClientConfiguration
object to override these defaults:
const options = {
long_type: "number",
max_attempts: 5,
max_backoff: 1000,
secret: "FAUNA_SECRET",
status_events: true,
};
client.stream(fql`Product.all().toStream()`, options)
For supported properties, see StreamClientConfiguration in the API reference.
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!