UDFs as Query Builders in FaunaDB’s FQL

For a recent project, my team created an event-based app that would be used for just a few weeks. We wanted to be able to answer questions such as, “How many new accounts were added today?” and subsequently, “How many actions did each account take today?” during the event to get a feel for the traffic.

I wrote a few FQL queries to our Fauna database and quickly found that I was duplicating a lot of the same logic. I wanted to be able to say “do this operation on this dataset” instead of rewriting it.

The best way to wrap up a piece of logic in FQL is a User Defined Function (UDF). That’s right, stored procedures are back. UDFs are great for all sorts of reasons so you should use them liberally!

My purpose for today is to demonstrate how UDFs can also be used to improve developer ergonomics for building queries and functions. By creating a handful of helper UDFs, I was able to make my queries more concise and readable, and easier to write.

Query Builder Functions

GetAll

When I first started learning FQL, I kept asking, “How do I just get all the documents?”

If you are coming from SQL, you may want to write something like “Select * from Accounts,” but that is not how it works in FQL. Check out this handy guide for more details.
Once I got used to thinking about querying indexes instead of collections, I quickly wanted an alias for getting all the documents in whatever set or page I gave it.

CreateFunction({
    name: "GetAll",
    body: Query(
        Lambda("refs", Map(Var("refs"), Lambda("x", Get("x"))))
    )
})

Example usage:

Call(Function("GetAll"), Paginate(Documents(Collection("accounts"))))

This is a simple builder function, but it will come in handy.

After

For realtime debugging or traffic tracking, it is useful to be able to write queries in terms of the timeframe you care about. You can use the options for Paginate to order the documents by time, but this gets unwieldy as collections grow. Here is a little function to filter by when the documents were added.

CreateFunction({
    name: "After",
    body: Query(
        Lambda(["refs", "time"],
        Filter(
            Var("refs"),
            GTE(Epoch(Select("ts", Var("x")), "microsecond"), Var("time"))
            )
        )
    )
})

Example usage:

Call(Function("After"), [Paginate(Documents(Collection("accounts"))), Time("2021-09-19T10:00:00Z")])

UDFs as Query Builders

There are many ways UDFs can improve your developer experience and code quality in FQL. The next time you find yourself writing the same logic in multiple places, write an UDF for it!

Related Posts