FaunaDB seems so ergonomic with React Hooks

A presentation of FaunaDB really impressed me recently, showing me how easy it is to use the composable FQL on the client with React Hooks. It really is built for the JAM Stack.

FaunaDB seems so ergonomic with React Hooks
Photo by benjamin lehman / Unsplash

I have used FaunaDB before, but only on the server in a small personal project. I had written a little analytics code, that recorded site visitors. I wanted to see if I could use that to replace a third party analytics library. Overall it worked well, though FQL took some effort to wrap my head around.

A recent talk by Brecht De Rooms and Eric Wyne made me realize just how truly easy and well-thought-out FaunaDB is. Here is the video from Next.js Conf 2020.

In the video Eric shows how a simple route on a server, can authenticate the user. This works because FaunaDB has built in authentication and roles. Once the server authenticates the user, it returns the users secret that allows the client to do all further actions, as that user. All the code below is from Eric’s MDX deck used during the presentation.


This is a sample pages/api/login.js file. Where the handle and password is passed to the serverless function to return a client secret as simple text.

import { Client, query as q } from 'faunadb';

const client = new Client({
    secret: 'fnAD4lN6LsACDZCBTVS5LkxWHMSpSTAuDLTImMe7',

const handler = async (req, res) => {
    const { handle, password } = req.body;
    // Match user by handle
    const matchedUser = q.Match(q.Index('userByHandle'), handle);
    // Use password to get user secret
        .query(q.Login(matchedUser, { password }))
        .then(({ secret }) => res.send(secret));

export default handler;


On the client the login route is called to get the clients secret. This is stored in the clients state for future queries. The thing that makes this elegant is the useQuery() hook that takes a FaunaDB FQL query and executes it as the logged in client.

import { Client, query as q } from 'faunadb';
import useSwr from 'swr';
import create from 'zustand';

const useStore = create(() => ({ secret: '' }));

export const login = async (handle, password) => {
    const secret = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ handle, password }),
    }).then(r => r.text());
    useStore.setState({ secret });

export function useQuery(query) {
    const secret = useStore(state => state.secret);
    const key = secret ? [secret, JSON.stringify(query)] : null;
    return useSwr(key, secret => new Client({ secret }).query(query));

export const useIdentity = () => useQuery(q.Get(q.Identity()));

// Usage
// const { data, error, isValidating } = useIdentity();

In the example above, the useIdentity() hook can just execute the q.Get(q.Identity()) query without having to worry about the login aspects.

FaunaDB is truly built for the JAM Stack.

The simplicity here makes this highly maintainable. I was impressed with FaunaDB when I first used it almost 2 years ago. This presentation just enforces that, and proves that FaunaDB is truly built for the JAM Stack.