Go client driver for Fauna
Version: 2.0.1 | Repository: fauna/fauna-go |
---|
Fauna’s Go client driver lets you run FQL queries from Go 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 version. |
Installation
The driver is available on pkg.go.dev and the fauna/fauna-go GitHub repository. To install it, run:
go get github.com/fauna/fauna-go/v2
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()
package main
import (
"fmt"
"github.com/fauna/fauna-go/v2"
)
func main() {
// Initialize the client to connect to Fauna
client := fauna.NewClient(
"YOUR_FAUNA_SECRET",
fauna.DefaultTimeouts(),
)
// Compose a query
query, _ := fauna.FQL(`
Product.sortedByPriceLowToHigh() {
name,
description,
price
}
`, nil)
res, err := client.Query(query)
if err != nil {
panic(err)
}
jsonData, _ := json.Marshal(res.Data)
fmt.Println(string(jsonData))
}
Connect to Fauna
To connect to Fauna, initialize a Client
instance.
The NewDefaultClient()
method initializes a client using:
-
A Fauna key, access token, or JWT in the
FAUNA_SECRET
environment variable -
A Fauna endpoint in the
FAUNA_ENDPOINT
environment variable -
Default client configuration options
client, clientErr := fauna.NewDefaultClient()
if clientErr != nil {
panic(clientErr)
}
To pass configuration options, use NewClient()
to initialize the client:
client := fauna.NewClient(
"YOUR_FAUNA_SECRET", // Your key, access token, or JWT
fauna.DefaultTimeouts(),
)
NewClient()
requires secret
and timeouts
arguments. For timeouts and more
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, _ := fauna.FQL(`Product.sortedByPriceLowToHigh()`, nil)
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 Go variables as map[string]any
to FQL
. You
can escape a variable by prepending an additional $
.
// Create a native Go var
collectionName := "Product"
// Pass the var to an FQL query
query, _ := fauna.FQL(`
let collection = Collection(${collectionName})
collection.sortedByPriceLowToHigh()
`, map[string]any{"collectionName": collectionName})
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
func getProduct(name string) *fauna.Query {
subquery, _ := fauna.FQL(`
Product.byName(${name}).first()
`, map[string]any{"name": name})
return subquery
}
func main() {
client := fauna.NewClient(
"YOUR_FAUNA_SECRET",
fauna.DefaultTimeouts(),
)
// Use the subquery in another FQL query
query, _ := fauna.FQL(`
let product = ${getProduct}
product?.update({
name: "pizza pie"
})
`, map[string]any{"getProduct": getProduct("pizza")})
client.Query(query)
}
Structs
The driver supports user-defined structs:
type Product struct {
Name string `fauna:"name"`
Description string `fauna:"description"`
Price float64 `fauna:"price"`
}
func main() {
client := fauna.NewClient(
"YOUR_FAUNA_SECRET",
fauna.DefaultTimeouts(),
)
newProduct := Product{"key limes", "Organic, 1 ct", 0.95}
query, _ := fauna.FQL(`Product.create(${product})`, map[string]any{"product": newProduct})
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, _ := fauna.FQL(`
Product
.byName("limes")
.pageSize(2)
`, nil)
paginator := client.Paginate(query)
for {
page, _ := paginator.Next()
var pageItems []any
page.Unmarshal(&pageItems)
for _, item := range pageItems {
fmt.Println(item)
}
if !paginator.HasNext() {
break
}
}
Query statistics
Successful query responses and the following error types include query statistics:
-
ErrAbort
-
ErrAuthentication
-
ErrAuthorization
-
ErrContendedTransaction
-
ErrInvalidRequest
-
ErrQueryCheck
-
ErrQueryRuntime
-
ErrQueryRuntime
-
ErrQueryTimeout
-
ErrServiceInternal
-
ErrServiceTimeout
-
ErrThrottling
query, _ := fauna.FQL(`"Hello world"`, nil)
res, err := client.Query(query)
if err != nil {
if faunaErr, ok := err.(*fauna.ErrQueryCheck); ok {
jsonData, _ := json.Marshal(faunaErr.QueryInfo.Stats)
fmt.Println(string(jsonData))
}
panic(err)
}
jsonData, _ := json.Marshal(res.Stats)
fmt.Println(string(jsonData))
Client configuration
The Client
instance comes with reasonable configuration defaults. We recommend
using the defaults in most cases.
If needed, you can use NewClient()
to configure the client and override the
defaults. This also lets you set default Query options.
secret := "YOUR_FAUNA_SECRET"
timeouts := fauna.Timeouts{
QueryTimeout: time.Minute,
ClientBufferTimeout: time.Second * 30,
ConnectionTimeout: time.Second * 10,
IdleConnectionTimeout: time.Minute * 5,
}
client := fauna.NewClient(
// Configure the client
secret,
timeouts,
fauna.URL("https://db.fauna.com"),
fauna.AdditionalHeaders(map[string]string{
"foo": "bar",
}),
fauna.Linearized(false),
fauna.MaxAttempts(5),
fauna.MaxBackoff(time.Minute),
fauna.MaxContentionRetries(5),
// Set default query options
fauna.DefaultTypecheck(true),
fauna.QueryTags(map[string]string{
"name": "hello world query",
}),
fauna.QueryTimeout(time.Second*60),
)
The following table outlines supported NewClient()
parameters.
Parameter | Type | Required | Description |
---|---|---|---|
|
|
Yes |
Fauna key, access token, or JWT used to authorize requests. |
|
Yes |
Struct containing timeouts for the client. See Timeouts. |
|
|
Configuration functions for the client. See Configuration functions. |
Timeouts
NewClient()
requires a timeouts
argument. The argument must contain a
fauna.Timeouts
struct:
timeouts := fauna.Timeouts{
QueryTimeout: time.Second * 5,
ClientBufferTimeout: time.Second * 5,
ConnectionTimeout: time.Second * 5,
IdleConnectionTimeout: time.Second * 5,
}
client := fauna.NewClient(
"YOUR_FAUNA_SECRET",
timeouts,
)
For default timeouts, use fauna.DefaultTimeouts()
:
client := fauna.NewClient(
"YOUR_FAUNA_SECRET",
fauna.DefaultTimeouts(),
)
The following table outlines supported fields for a fauna.Timeouts
struct and
their defaults.
Field name | Type | Required | Description |
---|---|---|---|
|
|
Maximum amount of time Fauna runs a query before marking it as failed. Defaults to 5 seconds. Maps to the |
|
|
|
Additional time beyond |
|
|
|
Time to wait for the connection to Fauna to complete. Defaults to 5 seconds. |
|
|
|
Maximum amount of time an idle (keep-alive) connection remains open before closing. Defaults to 5 seconds. |
Configuration functions
To configure the client and set default query options, pass
one or more ClientConfigFn
functions to NewClient()
:
client := fauna.NewClient(
"YOUR_FAUNA_SECRET",
fauna.DefaultTimeouts(),
// Start configuration functions
fauna.URL("https://db.fauna.com"),
fauna.AdditionalHeaders(map[string]string{
"foo": "bar",
}),
fauna.Linearized(false),
fauna.MaxAttempts(5),
fauna.MaxBackoff(time.Minute),
fauna.MaxContentionRetries(5),
// Configuration functions for
// default query options
fauna.DefaultTypecheck(true),
fauna.QueryTags(map[string]string{
"name": "hello world query",
}),
fauna.QueryTimeout(time.Second*60),
)
The following table outlines supported ClientConfigFn
functions. It
includes the default used if a function isn’t provided.
Function | Description | Default |
---|---|---|
|
Sets the URL for the Fauna endpoint. Accepts a |
|
|
Sets additional HTTP headers to include in requests. Accepts a
|
|
|
Accepts a Maps to the |
|
|
Sets the maximum number of retry attempts for a query. Accepts an |
|
|
Sets the maximum time to wait before retrying a query if the request returns
a 429 HTTP status code. Accepts a |
20 seconds |
|
Sets the maximum number of times Fauna retries a query after a
contention error. These are server-side retries. Accepts an Maps to the
|
|
|
Accepts a If |
The database’s type checking setting |
|
Key-value tags used to identify the query. Accepts a Query tags are included in query logs and the response body for successful queries. The tags are typically used for monitoring. Maps to the |
|
|
Sets the maximum amount of time Fauna runs a query before marking it as
failed. Accepts a Maps to the |
The |
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 fauna.MaxBackoff
configuration
function to set the maximum time between retries. Similarly, use
fauna.MaxAttempts
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()
-
Client.Stream()
To override these defaults, pass one or more QueryOptFn
functions
to the method:
options := []fauna.QueryOptFn{
fauna.Tags(map[string]string{
"name": "hello world query",
}),
fauna.Timeout(time.Minute),
fauna.Traceparent("00-750efa5fb6a131eb2cf4db39f28366cb-000000000000000b-00"),
fauna.Typecheck(true),
}
query, _ := fauna.FQL(`"Hello world"`, nil)
client.Query(query, options...)
The following table outlines supported QueryOptFn
functions.
Function | Description |
---|---|
|
Key-value tags used to identify the query. Accepts a Query tags are included in query logs and the response body for successful queries. The tags are typically used for monitoring. |
|
Sets the maximum amount of time Fauna runs a query before marking it as
failed. Accepts a |
|
Sets a W3C-compliant traceparent ID for the request. Accepts a The traceparent ID is included in query logs. Traceparent IDs are typically used for monitoring. |
|
Accepts a If |
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 a query that produces a stream token
to Client.Stream()
:
type Product struct {
Name string `fauna:"name"`
Description string `fauna:"description"`
Price float64 `fauna:"price"`
}
func main() {
client := fauna.NewClient(
"YOUR_FAUNA_SECRET",
fauna.DefaultTimeouts(),
)
streamQuery, _ := fauna.FQL("Product.all().toStream()", nil)
events, err := client.Stream(streamQuery)
if err != nil {
panic(err)
}
defer events.Close()
var event fauna.Event
for {
err := events.Next(&event)
if err != nil {
panic(err)
}
switch event.Type {
case fauna.AddEvent, fauna.UpdateEvent, fauna.RemoveEvent:
var product Product
if err = event.Unmarshal(&product); err != nil {
panic(err)
}
fmt.Println(product)
}
}
}
In query results, the driver represents stream tokens as fauna.Stream
values.
To start a stream from a query result, call Client.Subscribe()
on a
fauna.Stream
value. This lets you output a stream alongside normal query
results:
type Product struct {
Name string `fauna:"name"`
Description string `fauna:"description"`
Price float64 `fauna:"price"`
}
func main() {
client := fauna.NewClient(
"YOUR_FAUNA_SECRET",
fauna.DefaultTimeouts(),
)
dataLoad, _ := fauna.FQL(`
let products = Product.all()
{
Products: products.toArray(),
Stream: products.toStream()
}
`, nil)
data, err := client.Query(dataLoad)
if err != nil {
panic(err)
}
queryResult := struct {
Products []Product
Stream fauna.Stream
}{}
if err := data.Unmarshal(&queryResult); err != nil {
panic(err)
}
fmt.Println("Existing products:")
for _, product := range queryResult.Products {
fmt.Println(product)
}
events, err := client.Subscribe(queryResult.Stream)
if err != nil {
panic(err)
}
defer events.Close()
fmt.Println("Products from streaming:")
var event fauna.Event
for {
err := events.Next(&event)
if err != nil {
panic(err)
}
switch event.Type {
case fauna.AddEvent, fauna.UpdateEvent, fauna.RemoveEvent:
var product Product
if err = event.Unmarshal(&product); err != nil {
panic(err)
}
fmt.Println(product)
}
}
}
Stream options
The Client configuration sets default query options for Client.Stream()
.
To override these options, see Query options.
The Client.Subscribe()
method accepts a fauna.StartTime
function. You can
use fauna.StartTime
to restart a stream after disconnection.
streamQuery, _ := fauna.FQL(`Product.all().toStream()`, nil)
client.Subscribe(streamQuery, fauna.StartTime(1710968002310000))
Function | Description |
---|---|
|
Sets the stream start time. Accepts an The start time is typically the time the stream disconnected. The start time must be later than the creation time of the stream token. The
period between the stream restart and the start time argument can’t exceed the
|
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!