Back to Fluxbase

Integration Guide

The complete technical reference for connecting your apps to Fluxbase. Covers authentication, the SQL API, real-time WebSockets, file storage, team management, and row-level security.

v4.2PostgreSQLMySQLREST + WebSocket

Getting Started

Every Fluxbase integration requires three values, found in your Project Settings:

API Key

Bearer token from Settings → API Keys. Scoped per project.

Project ID

Unique project identifier visible in the URL and Settings.

Base URL

https://fluxbase.vercel.app — all REST endpoints live here.

WebSocket URL

wss://fluxbase-realtime.onrender.com — for real-time events.

Environment Setup

.env.local
FLUXBASE_API_KEY=flx_live_xxxxxxxxxxxxxxxxxxxx
FLUXBASE_PROJECT_ID=51c04beb753a42f3
NEXT_PUBLIC_WS_URL=wss://fluxbase-realtime.onrender.com
Never expose your API key on the client side. Always call Fluxbase from your server (Next.js API routes, Express, etc.) and keep your key in server-only environment variables.

Authentication

All REST API requests must include an Authorization header with a valid Bearer token. Requests missing this header return 401 Unauthorized.

Request Header
Authorization: Bearer flx_live_xxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

API Key Scopes

ScopeAccessRecommended For
readSELECT onlyDashboards, public APIs
readwriteSELECT, INSERT, UPDATEBackend services
adminFull DDL + DML accessMigration scripts, init scripts
Rotate API keys any time from Settings → API Keys. Old keys are invalidated immediately.

Core SQL API

Execute any SQL statement against your project database via a single unified endpoint.

POST/api/execute-sql
Request Body
{
  "projectId": "YOUR_PROJECT_ID",
  "query": "SELECT * FROM users LIMIT 10;"
}
Success Response (200)
{
  "success": true,
  "result": {
    "rows": [
      { "id": 1, "name": "Alice", "email": "alice@example.com" }
    ],
    "columns": ["id", "name", "email"],
    "rowCount": 1,
    "message": null
  },
  "executionInfo": {
    "time": "11ms",
    "rowCount": 1,
    "operation": "SELECT"
  }
}
Error Response (200 with error)
{
  "success": false,
  "error": {
    "message": "relation "usrs" does not exist",
    "code": "SQL_EXEC_ERROR",
    "details": "ERROR:  42P01"
  }
}
SQL errors return HTTP 200 with success: false. Always check the success field — don't rely solely on HTTP status codes.

Query Parameters

FieldTypeRequiredDescription
projectIdstringYesYour project's unique identifier.
querystringYesThe SQL statement to execute. Supports DDL and DML.
paramsarrayNoOptional parameterized values for $1, $2, … placeholders.

Language SDKs

No official SDK required — Fluxbase is a plain HTTP API. Here are copy-paste integration snippets for the most popular languages.

fluxbase.js
const BASE_URL = 'https://fluxbase.vercel.app';
const API_KEY  = process.env.FLUXBASE_API_KEY;
const PROJECT  = process.env.FLUXBASE_PROJECT_ID;

async function query(sql, params = []) {
  const res = await fetch(`${BASE_URL}/api/execute-sql`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${API_KEY}`,
    },
    body: JSON.stringify({ projectId: PROJECT, query: sql, params }),
  });

  const json = await res.json();
  if (!json.success) throw new Error(json.error?.message ?? 'Query failed');
  return json.result.rows;
}

// Usage
const users = await query('SELECT id, name FROM users WHERE active = $1', [true]);
console.log(users);

Real-time (WebSocket)

Fluxbase uses a persistent WebSocket connection to push live database events to connected clients. Subscribe once and receive row changes, schema updates, and custom broadcasts — with automatic reconnection.

Connect & Subscribe

Open one WebSocket and join a project room. All events are multiplexed over a single connection.

Receive Events

Get db_event messages for INSERT/UPDATE/DELETE and schema_update for DDL changes.

Auto-Reconnect

Built-in exponential backoff (1s → 2s → 4s → 15s max). Subscriptions restore automatically.

.env.local
NEXT_PUBLIC_WS_URL=wss://fluxbase-realtime.onrender.com
realtime.js
const WS_URL    = process.env.NEXT_PUBLIC_WS_URL ?? 'wss://fluxbase-realtime.onrender.com';
const PROJECT_ID = 'YOUR_PROJECT_ID';

let socket;
let reconnectDelay = 1000;

function connect() {
  socket = new WebSocket(WS_URL);

  socket.onopen = () => {
    reconnectDelay = 1000; // reset on successful connection
    socket.send(JSON.stringify({
      type:   'subscribe',
      roomId: `project_${PROJECT_ID}`,
    }));
    console.log('[WS] Connected and subscribed.');
  };

  socket.onmessage = ({ data }) => {
    const msg = JSON.parse(data);

    switch (msg.type) {
      case 'subscribed':
        console.log('[WS] Subscription confirmed.');
        break;

      case 'db_event': {
        const { operation, table, record, old } = msg.payload ?? {};
        if (operation === 'INSERT') console.log(`+  ${table}`, record);
        if (operation === 'UPDATE') console.log(`~  ${table}`, { old, record });
        if (operation === 'DELETE') console.log(`-  ${table}`, record);
        break;
      }

      default:
        if (msg.payload?.event_type === 'schema_update') {
          console.log('[WS] Schema changed — refresh your table list.');
        }
    }
  };

  socket.onclose = () => {
    console.warn(`[WS] Disconnected. Reconnecting in ${reconnectDelay}ms...`);
    setTimeout(connect, reconnectDelay);
    reconnectDelay = Math.min(reconnectDelay * 2, 15000);
  };

  socket.onerror = (err) => console.error('[WS] Error:', err);
}

connect();

Event Reference

msg.typepayload detailsWhen it fires
subscribed—Server confirmed room subscription.
db_eventoperation: INSERTA row was inserted via SQL or Table Editor.
db_eventoperation: UPDATEA row was modified.
db_eventoperation: DELETEA row was removed.
db_eventevent_type: schema_updateDDL executed (CREATE / DROP / ALTER / RENAME / TRUNCATE).

Storage v2

Secure AWS S3-backed file storage with logically isolated buckets, private-by-default access, and short-lived pre-signed URLs.

Bucket Management

MethodEndpointAction
GET/api/storage/buckets?projectId=…List all buckets + total size
POST/api/storage/bucketsCreate a bucket
PATCH/api/storage/bucketsRename a bucket
DELETE/api/storage/bucketsDelete a bucket (must be empty)
POST /api/storage/buckets — Create Bucket
{
  "projectId": "YOUR_PROJECT_ID",
  "name": "profile-photos",
  "isPublic": false
}
// Name rules: lowercase alphanumeric, hyphens, underscores, 1–63 chars

File Operations

MethodEndpointAction
POST/api/storage/uploadUpload file (multipart/form-data)
GET/api/storage/files?bucketId=…&projectId=…List files in a bucket
GET/api/storage/url?s3Key=…&projectId=…Get 15-min pre-signed download URL
DELETE/api/storage/filesDelete a file (S3 + database)

Upload a File

Send multipart/form-data — required fields: file, bucketId, projectId. bucketId accepts the bucket UUID or name.

cURL — Multipart Upload
curl -X POST "https://fluxbase.vercel.app/api/storage/upload" \
  -H "Authorization: Bearer $FLUXBASE_API_KEY" \
  -F "file=@avatar.jpg" \
  -F "bucketId=profile-photos" \
  -F "projectId=YOUR_PROJECT_ID"
Upload Response
{
  "success": true,
  "file": {
    "id": "uuid-here",
    "name": "avatar.jpg",
    "s3_key": "YOUR_PROJECT_ID/bucket-uuid/avatar.jpg",
    "size": 204800,
}

Delete a File

DELETE/api/storage/delete?s3Key=…&projectId=…
Bucket names must be unique per project. Create and manage buckets from the Storage section in your project dashboard.

Team & Invitations

Manage project collaborators and send role-based invitations programmatically. All team endpoints require admin-level privileges.

List Team Members

GET/api/team?projectId=YOUR_PROJECT_ID
Response
{
  "members": [
    { "userId": "usr_abc", "email": "alice@acme.com", "displayName": "Alice", "role": "admin", "joinedAt": "2026-01-10T09:00:00Z" }
  ],
  "invites": [
    { "id": "inv_xyz", "email": "bob@acme.com", "role": "developer", "invitedAt": "2026-04-14T11:00:00Z" }
  ]
}

Send an Invitation

POST/api/team
Request Body
{
  "projectId": "YOUR_PROJECT_ID",
  "email": "newmember@company.com",
  "role": "developer"
}
RolePermissions
adminFull access — manage members, settings, billing, and data.
developerRead/write data, manage schemas. Cannot manage billing or members.
viewerRead-only access to data and the dashboard.

Accept / Reject an Invitation

POST/api/team/invites/accept
Request Body
{
  "inviteId": "inv_xyz",
  "status": "accepted"
}

Remove a Member

DELETE/api/team?projectId=YOUR_PROJECT_ID&userId=usr_abc
Invitations are email-case-insensitive. Sending a new invite automatically resets any previous pending, accepted, or rejected state for that email in the project.

Webhooks

Webhooks are outbound HTTP POST requests sent from Fluxbase to your server when data events occur. Ideal for serverless functions (Vercel, AWS Lambda, Cloudflare Workers).

Supported Events

  • row.inserted
  • row.updated
  • row.deleted
  • schema.changed

Signature Verification

Register a Secret in your webhook config. Fluxbase signs every delivery with an X-Fluxbase-Signature HMAC-SHA256 header for you to verify.

Webhook Payload
{
  "event_type": "row.inserted",
  "project_id": "YOUR_PROJECT_ID",
  "table_id": "orders",
  "timestamp": "2026-04-14T12:00:00.000Z",
  "data": {
    "new": { "id": 123, "customer_id": 7, "amount": 59.99, "status": "pending" },
    "old": null
  }
}

Verifying the Signature

webhook-handler.js
import crypto from 'crypto';

export async function POST(req) {
  const rawBody = await req.text();
  const sig     = req.headers.get('x-fluxbase-signature') ?? '';
  const secret  = process.env.FLUXBASE_WEBHOOK_SECRET;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  if (sig !== `sha256=${expected}`) {
    return new Response('Unauthorized', { status: 401 });
  }

  const event = JSON.parse(rawBody);
  console.log('[Webhook]', event.event_type, event.data.new);

  // Your business logic here...
  return new Response('OK', { status: 200 });
}

Error Codes

Fluxbase uses standard HTTP status codes alongside machine-readable error codes in the response body.

HTTP StatusError CodeMeaning & Fix
401AUTH_REQUIREDAPI key missing or malformed. Include Authorization: Bearer <key>.
401TOKEN_EXPIREDSession token has expired. Re-authenticate to get a new token.
403SCOPE_MISMATCHAPI key does not have access to this project or resource.
403PROJECT_SUSPENDEDProject is suspended due to plan limits or billing. Upgrade your plan.
403INSUFFICIENT_PERMISSIONSOnly admins can perform this action (e.g., inviting members).
404PROJECT_NOT_FOUNDNo project found for the given projectId. Check your ID.
404USER_NOT_FOUNDInvitee email does not match any registered Fluxbase account.
429RATE_LIMIT50 requests per 10 seconds exceeded. Implement exponential backoff.
500INTERNAL_ERRORUnexpected server error. These are logged and auto-reported.
503DATABASE_CONNECTION_ERRORTransient database connectivity issue. Retry after a few seconds.
200SQL_EXEC_ERRORSQL syntax or logic error. Check the error.details field for the Postgres error.

Row Level Security

Row Level Security (RLS) enforces access rules at the database engine level. Your backend API can never return data that violates a policy — regardless of the SQL query sent.
Auth Identity

Your logged-in user ID is injected via SET LOCAL fluxbase.auth_uid before each query.

Policy Evaluation

PostgreSQL runs your USING expression (e.g. user_id = auth.uid()) before returning any row.

Silent Filtering

Rows that fail the policy are silently excluded — no errors, just scoped results.

Step-by-Step Setup

01
Open RLS Dashboard

Navigate to Database → Row Level Security in your project sidebar. All tables are listed.

02
Create a Policy

Choose a table, select command scope (ALL / SELECT / INSERT / UPDATE / DELETE), then write your USING expression.

03
Enable the Policy

Toggle the switch. Fluxbase immediately runs ALTER TABLE … ENABLE ROW LEVEL SECURITY and CREATE POLICY.

04
Test in SQL Editor

Run SELECT * FROM your_table — you'll only see rows that pass the policy for the authenticated user.

Common Policy Patterns

User Owns Their Rows

Classic pattern for posts, orders, and profiles.

USING expression
user_id = auth.uid()

Multi-Tenant (Org-Scoped)

All members of an organization share visibility. Set auth.uid() to the org ID at the API level.

USING expression
org_id = auth.uid()

Public Read, Owner Write

Create two policies on the same table with different command scopes.

Policy 1 — SELECT (anyone)
true
Policy 2 — INSERT / UPDATE / DELETE (owner only)
author_id = auth.uid()

Full Lockdown

Useful for internal audit tables that no API caller should read.

USING expression
false

Troubleshooting

ProblemCauseFix
Query returns 0 rowsPolicy never matchesTemporarily set USING to true to confirm RLS is the cause.
Policy toggle failsColumn in expression missingEnsure user_id (or the referenced column) exists in the table.
User can't see own rowsType mismatch (INT vs TEXT)Cast explicitly: user_id::text = auth.uid()
Admin locked outFORCE ROW LEVEL SECURITY activeRun ALTER TABLE … NO FORCE ROW LEVEL SECURITY in the SQL Editor.

Still have questions?

Our team is available for architecture reviews and custom integration support.