Skip to content

Build Your First API

This tutorial walks you through building a complete REST API with Fluxbase, from creating your first table to querying data with authentication.

A simple task management API with:

  • A tasks table
  • CRUD operations via REST API
  • User authentication
  • Row-Level Security (RLS) so users only see their own tasks
  • Fluxbase running locally (see Quick Start)
  • Admin account created
  • Node.js installed (for the TypeScript SDK examples)
  1. Open the admin dashboard at http://localhost:8080/admin

  2. Navigate to Tables in the sidebar

  3. Click Create Table

  4. Enter the table details:

    • Name: tasks
    • Schema: public
  5. Add the following columns:

ColumnTypeNullableDefault
iduuidNogen_random_uuid()
user_iduuidNoauth.uid()
titletextNo-
descriptiontextYes-
completedbooleanNofalse
created_attimestamptzNonow()
  1. Click Create Table

Alternatively, run this SQL in the dashboard’s SQL editor:

CREATE TABLE public.tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL DEFAULT auth.uid(),
title TEXT NOT NULL,
description TEXT,
completed BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Create an index for efficient user queries
CREATE INDEX idx_tasks_user_id ON public.tasks(user_id);

The anon key authenticates your SDK with Fluxbase. It’s a JWT token with the anon role.

  1. Open the .env file in your deploy/ directory (created by generate-keys.sh)
  2. Copy the FLUXBASE_ANON_KEY value - you’ll need it in the next step

Create a new Node.js project:

Terminal window
mkdir my-fluxbase-app
cd my-fluxbase-app
npm init -y
npm install @nimbleflux/fluxbase-sdk

Create a file index.ts:

import { createClient } from "@nimbleflux/fluxbase-sdk";
// Replace with your server URL and anon key (from .env file)
const fluxbase = createClient("http://localhost:8080", "your-anon-key-here");
async function main() {
// Test the connection
const { data, error } = await fluxbase.from("tasks").select("*");
if (error) {
console.error("Error:", error.message);
return;
}
console.log("Tasks:", data);
}
main();

Run it:

Terminal window
npx tsx index.ts

You should see an empty array [] since we haven’t added any tasks yet.

RLS ensures users can only access their own data.

  1. In the dashboard, go to Tables > tasks
  2. Click the RLS Policies tab
  3. First, enable RLS on the table:
ALTER TABLE public.tasks ENABLE ROW LEVEL SECURITY;
  1. Create policies for each operation:
-- Users can view their own tasks
CREATE POLICY "Users can view own tasks"
ON public.tasks FOR SELECT
USING (auth.uid() = user_id);
-- Users can create their own tasks
CREATE POLICY "Users can create own tasks"
ON public.tasks FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Users can update their own tasks
CREATE POLICY "Users can update own tasks"
ON public.tasks FOR UPDATE
USING (auth.uid() = user_id);
-- Users can delete their own tasks
CREATE POLICY "Users can delete own tasks"
ON public.tasks FOR DELETE
USING (auth.uid() = user_id);

Enable signups in the dashboard:

  1. Go to Settings > Authentication
  2. Enable Allow Signups
  3. Save changes

Update your index.ts to sign up a user:

import { createClient } from "@nimbleflux/fluxbase-sdk";
const fluxbase = createClient("http://localhost:8080", "your-anon-key-here");
async function main() {
// Sign up a new user
const { data: authData, error: authError } = await fluxbase.auth.signUp({
email: "user@example.com",
password: "securepassword123",
});
if (authError) {
console.error("Auth error:", authError.message);
return;
}
console.log("User created:", authData.user?.email);
console.log("Access token:", authData.session?.access_token);
}
main();

Run it to create the user.

Now let’s build a complete example:

import { createClient } from "@nimbleflux/fluxbase-sdk";
const fluxbase = createClient("http://localhost:8080", "your-anon-key-here");
async function main() {
// Sign in
const { data: auth, error: authError } = await fluxbase.auth.signIn({
email: "user@example.com",
password: "securepassword123",
});
if (authError) {
console.error("Login failed:", authError.message);
return;
}
console.log("Logged in as:", auth.user?.email);
// Create a task
const { data: newTask, error: createError } = await fluxbase
.from("tasks")
.insert({
title: "Learn Fluxbase",
description: "Complete the first API tutorial",
})
.select()
.single();
if (createError) {
console.error("Create error:", createError.message);
return;
}
console.log("Created task:", newTask);
// List all tasks
const { data: tasks, error: listError } = await fluxbase
.from("tasks")
.select("*")
.order("created_at", { ascending: false });
if (listError) {
console.error("List error:", listError.message);
return;
}
console.log("All tasks:", tasks);
// Update a task
const { data: updated, error: updateError } = await fluxbase
.from("tasks")
.update({ completed: true })
.eq("id", newTask.id)
.select()
.single();
if (updateError) {
console.error("Update error:", updateError.message);
return;
}
console.log("Updated task:", updated);
// Delete a task
const { error: deleteError } = await fluxbase
.from("tasks")
.delete()
.eq("id", newTask.id);
if (deleteError) {
console.error("Delete error:", deleteError.message);
} else {
console.log("Task deleted");
}
}
main();

You can also use the REST API directly:

Terminal window
# Get an access token
TOKEN=$(curl -s -X POST http://localhost:8080/api/v1/auth/signin \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"securepassword123"}' \
| jq -r '.access_token')
# Create a task
curl -X POST http://localhost:8080/api/v1/tables/public/tasks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"My first task","description":"Created via REST API"}'
# List tasks
curl http://localhost:8080/api/v1/tables/public/tasks \
-H "Authorization: Bearer $TOKEN"
# Filter tasks
curl "http://localhost:8080/api/v1/tables/public/tasks?completed=eq.false" \
-H "Authorization: Bearer $TOKEN"

The REST API supports PostgREST-compatible operators:

OperatorDescriptionExample
eqEqual?status=eq.active
neqNot equal?status=neq.deleted
gtGreater than?created_at=gt.2024-01-01
gteGreater or equal?priority=gte.5
ltLess than?priority=lt.3
lteLess or equal?priority=lte.10
likePattern match?title=like.*urgent*
ilikeCase-insensitive pattern?title=ilike.*URGENT*
inIn list?status=in.(active,pending)
isIs null/true/false?deleted_at=is.null
// Filter by column value
const { data } = await fluxbase
.from("tasks")
.select("*")
.eq("completed", false);
// Multiple filters (AND)
const { data } = await fluxbase
.from("tasks")
.select("*")
.eq("completed", false)
.gte("priority", 5);
// Order and limit
const { data } = await fluxbase
.from("tasks")
.select("*")
.order("created_at", { ascending: false })
.limit(10);
// Select specific columns
const { data } = await fluxbase.from("tasks").select("id, title, completed");
// Count rows
const { count } = await fluxbase
.from("tasks")
.select("*", { count: "exact", head: true });

Congratulations! You’ve built your first Fluxbase API. Here’s what to explore next: