falcondatabase
Sign in

← Blog

Tutorial · 2 min read · kevin

Tutorial: use falcondatabase from React

A shared client, a small useQuery hook and an auth-aware component for React or Next.js apps.

The SDK is framework agnostic, so it fits naturally in React. In this tutorial we'll create one shared client, a reusable hook, and a component that reacts to sign in and sign out.

1. Add the SDK to your project

Download the module once into your source tree, so your bundler can import it:

curl -o src/falcondb.js http://falcondatabase.com/sdk/falcondb.js

Keep your settings in environment variables (Vite example):

# .env.local
VITE_FALCONDB_URL=http://falcondatabase.com
VITE_FALCONDB_REF=PROJECT_REF
VITE_FALCONDB_KEY=ANON_KEY

2. One shared client

// src/db.js
import { createClient } from "./falcondb.js"

export const db = createClient(
  import.meta.env.VITE_FALCONDB_URL,
  import.meta.env.VITE_FALCONDB_REF,
  import.meta.env.VITE_FALCONDB_KEY,
)

Create it once: the client keeps the user session, and every component should share it.

3. A useQuery hook

// src/useQuery.js
import { useEffect, useState, useCallback } from "react"

export function useQuery(buildQuery, deps = []) {
  const [state, setState] = useState({ data: null, count: 0, error: null, loading: true })

  const run = useCallback(async () => {
    setState((s) => ({ ...s, loading: true }))
    const { data, count, error } = await buildQuery()
    setState({ data, count, error, loading: false })
  }, deps)

  useEffect(() => { run() }, [run])
  return { ...state, refresh: run }
}
import { useState } from "react"
import { db } from "./db"
import { useQuery } from "./useQuery"

export function Products() {
  const [search, setSearch] = useState("")
  const { data, count, loading, error } = useQuery(
    () => db.from("products").select("id,name,price").ilike("name", `%${search}%`).order("name").limit(50),
    [search],
  )

  return (
    <section>
      <input value={search} onChange={(e) => setSearch(e.target.value)} placeholder="Search" />
      {loading && <p>Loading…</p>}
      {error && <p role="alert">{error.message}</p>}
      <p>{count} products</p>
      <ul>{data?.map((p) => <li key={p.id}>{p.name} — {p.price} €</li>)}</ul>
    </section>
  )
}

5. Follow the signed-in user

db.auth.onChange fires on sign in and sign out. Wrap it in a hook:

import { useEffect, useState } from "react"
import { db } from "./db"

export function useUser() {
  const [user, setUser] = useState(db.auth.user)
  useEffect(() => db.auth.onChange(setUser), [])   // returns the unsubscribe function
  return user
}

export function Account() {
  const user = useUser()
  if (!user) {
    return <button onClick={() => db.auth.signIn({ email: "ana@example.com", password: "secret123" })}>Sign in</button>
  }
  return <p>{user.email} <button onClick={() => db.auth.signOut()}>Sign out</button></p>
}

Notes for Next.js

  • Use the anon key in client components ("use client").
  • Use the service key only in server code (route handlers, server actions) and read it from a non-public variable, for example FALCONDB_SERVICE_KEY without the NEXT_PUBLIC_ prefix.
  • The REST API works the same with plain fetch on the server, see the REST API tutorial.

CORS

By default any origin may call the API. In production you can restrict it with the FALCONDB_CORS_ORIGINS setting, for example https://app.example.com.