FLXBL SDK

Type-safe client for your FLXBL instance. Zero dependencies, native fetch, works in Node.js 18+, Deno, Bun, and edge runtimes.

Installation

npm install @flxbl-dev/client

Quick Start

import { FlxblClient } from '@flxbl-dev/client';

const client = new FlxblClient({
  instanceUrl: 'https://api.flxbl.dev',
  apiKey: 'flxbl_your_api_key',
});

// Create a record
const product = await client.request('POST', '/api/v1/dynamic/Product', {
  name: 'Wireless Headphones',
  price: 299.99,
  inStock: true,
});

console.log(product); // { id: 'node_abc123', name: '...', ... }

Authentication

The SDK supports two authentication methods. Use API keys for server-side applications and access tokens (JWTs) for client-side or per-user sessions.

import { FlxblClient } from '@flxbl-dev/client';

// Option 1: API key (recommended for server-side)
const client = new FlxblClient({
  instanceUrl: 'https://api.flxbl.dev',
  apiKey: 'flxbl_your_api_key',
});

// Option 2: Access token (for client-side / user sessions)
const client2 = new FlxblClient({
  instanceUrl: 'https://api.flxbl.dev',
  accessToken: 'eyJhbGciOiJIUzI1NiIs...',
});

// You can also set or update the token dynamically
client2.setAccessToken('new_jwt_token_here');

Collections

The generated client (from flxbl generate) provides typed collection accessors with CRUD, query, and batch helpers. Vector search helpers are generated for entities with VECTOR fields, and relationship helpers are generated from schema relationships. Collections are typed as createCollection<T, TCreate, TUpdate>() with helpers like:

Method Description
query(options?) Query records with filters, search, sorting, pagination, counts, and traversals
findMany(options?) Alias-style list helper for paginated record queries
findFirst(options?) Return the first record matching the query options
create(data) Create a new record
update(id, data) Partial update of a record
delete(id) Delete a record by ID
count(where?) Count records matching an optional filter
exists(where?) Check whether any record matches an optional filter
batchCreate(data[]) Create multiple records in one request
batchUpdate(items[]) Update multiple records by ID in one request
batchDelete(ids[]) Delete multiple records by ID in one request
// With the generated typed client (from `flxbl generate`):
import { createFlxblClient } from './flxbl/_generated';

const db = createFlxblClient({
  instanceUrl: 'https://api.flxbl.dev',
  apiKey: 'flxbl_your_api_key',
});

// Typed CRUD operations
const products = await db.products.query({
  where: { inStock: { $eq: true } },
  search: 'wireless',
  select: ['id', 'name', 'price'],
  orderBy: 'price',
  orderDirection: 'DESC',
  limit: 20,
});

const product = await db.products.findFirst({
  where: { id: { $eq: 'node_abc123' } },
});

const newProduct = await db.products.create({
  name: 'New Widget',
  price: 49.99,
  inStock: true,
});

const updated = await db.products.update('node_abc123', {
  price: 39.99,
});

await db.products.delete('node_abc123');

Query Options

The query and findMany methods accept a QueryOptions<T> object with the following fields:

Field Type Description
where object Filter conditions using query operators
search string Full-text search string for searchable fields
select string[] Fields to include in the response
orderBy string Field name to sort by
orderDirection 'ASC' | 'DESC' Sort direction
offset number Number of items to skip
limit number Maximum items to return
includeCount boolean Include total count in response
traverse object[] Relationship traversals (relationship, direction, select, where)
const results = await db.products.query({
  // Filter conditions
  where: {
    status: { $eq: 'active' },
    price: { $lte: 100 },
    tags: { $contains: 'renewal' },
  },

  // Full-text search across searchable fields
  search: 'contract renewal',

  // Field selection
  select: ['name', 'price', 'description'],

  // Sorting
  orderBy: 'price',
  orderDirection: 'ASC',

  // Pagination
  offset: 0,
  limit: 20,
  includeCount: true,

  // Graph traversal
  traverse: [
    {
      relationship: 'BELONGS_TO',
      direction: 'out',
      select: ['name', 'slug'],
    },
  ],
});

console.log(results.items);  // Product[]
console.log(results.count);  // Total matching count

Filter Operators

Use these operators in where conditions:

Operator Description Example
$eq Equal { status: { $eq: 'active' } }
$neq Not equal { status: { $neq: 'archived' } }
$gt Greater than { price: { $gt: 100 } }
$gte Greater than or equal { price: { $gte: 100 } }
$lt Less than { price: { $lt: 50 } }
$lte Less than or equal { price: { $lte: 50 } }
$in In array { status: { $in: ['a', 'b'] } }
$notIn Not in array { status: { $notIn: ['x'] } }
$isNull Is null { deletedAt: { $isNull: true } }
$contains String contains { name: { $contains: 'phone' } }
$startsWith String starts with { name: { $startsWith: 'Pro' } }
$endsWith String ends with { email: { $endsWith: '.com' } }
$and Logical AND Combine multiple conditions
$or Logical OR Match any condition

Vector Search

Search entities by vector similarity using generated collection helpers or the base client.vectorSearch(). This requires a VECTOR field on the target entity and a query vector from your embedding model.

import { FlxblClient } from '@flxbl-dev/client';

// Semantic search using vector embeddings
const generatedResults = await db.documents.vectorSearch({
  field: 'embedding',
  vector: [0.1, 0.2, 0.3, /* ...1536 dimensions */],
  topK: 5,
  where: { status: { $eq: 'published' } },
  select: ['id', 'title', 'summary'],
});

const client = new FlxblClient({
  instanceUrl: 'https://api.flxbl.dev',
  apiKey: 'flxbl_your_api_key',
});

const baseResults = await client.vectorSearch<Document>('Document', {
  field: 'embedding',
  vector: [0.1, 0.2, 0.3, /* ...1536 dimensions */],
  topK: 5,
  where: { status: { $eq: 'published' } },
  select: ['id', 'title', 'summary'],
});

for (const result of generatedResults) {
  console.log(result.data.title);  // Document title
  console.log(result.score);       // Cosine similarity (0-1)
}
Option Type Description
field string Name of the VECTOR field (required)
vector number[] Query vector — must match the field's dimensions (required)
topK number Max results, 1–100 (default: 10)
where object Optional filter conditions
select string[] Optional fields to include

GraphQL

Execute raw GraphQL queries against your tenant's GraphQL endpoint:

// Execute raw GraphQL queries
const tenantId = 'your_tenant_id';

const result = await client.graphql<{
  products: Array<{ id: string; name: string; price: number }>;
}>(tenantId, `
  query {
    products(where: { inStock: { equals: true } }, limit: 10) {
      id
      name
      price
    }
  }
`);

console.log(result.data?.products);

Relationships

Manage graph relationships between entities using client.relationships():

// Manage relationships between entities
const rel = client.relationships('Worker', 'worker_123');

// Create a relationship
await rel.create('ASSIGNED_TO', 'project_456', {
  role: 'lead',
  allocatedHours: 24,
});

// List relationships with node and edge filters
const assignments = await rel.list('ASSIGNED_TO', {
  direction: 'out',
  where: { status: { $eq: 'active' } },
  edgeWhere: { role: { $eq: 'lead' } },
  orderBy: 'createdAt',
  orderDirection: 'DESC',
  limit: 20,
  offset: 0,
});

const relationshipId = assignments.items[0]._relationship.id;

// Update relationship properties by target id
await rel.update('ASSIGNED_TO', 'project_456', {
  role: 'reviewer',
});

// Update the exact relationship edge by id
await rel.updateById('ASSIGNED_TO', relationshipId, {
  role: 'owner',
});

// Delete a relationship by target id
await rel.delete('ASSIGNED_TO', 'project_456');

// Delete the exact relationship edge by id
await rel.deleteById('ASSIGNED_TO', relationshipId);

// Generated relationship helper
const workerRelationships = db.workers.relationships('worker_123');
const assignedTo = workerRelationships.assignedTo;
const workerAssignments = await assignedTo.list({
  where: { status: { $eq: 'active' } },
  edgeWhere: { role: { $eq: 'lead' } },
  limit: 10,
});

await assignedTo.updateById(relationshipId, {
  role: 'owner',
});

await assignedTo.deleteById(relationshipId);

console.log(assignments.items, workerAssignments.items);
List option Description
direction Relationship traversal direction, out, in, or both
where Filter related target-node fields
edgeWhere Filter relationship properties
orderBy Sort related target nodes by field
orderDirection Sort direction, ASC or DESC
limit Maximum relationships to return
offset Number of relationships to skip

Error Handling

The SDK throws typed errors for different failure scenarios. All errors extend FlxblApiError:

Error Class Status When
FlxblValidationError 400 Invalid input — includes field-level validationErrors
FlxblAuthError 401 Missing or invalid credentials
FlxblNotFoundError 404 Entity or record not found
FlxblApiError * Any other API error (403, 429, 500, etc.)
import {
  FlxblApiError,
  FlxblAuthError,
  FlxblValidationError,
  FlxblNotFoundError,
} from '@flxbl-dev/client';

try {
  await db.products.create({ name: '' });
} catch (error) {
  if (error instanceof FlxblValidationError) {
    // 400 — field-level validation errors
    console.log(error.validationErrors);
    // [{ field: 'name', message: 'Name is required' }]
  } else if (error instanceof FlxblAuthError) {
    // 401 — invalid or missing credentials
    console.log(error.message);
  } else if (error instanceof FlxblNotFoundError) {
    // 404 — entity or record not found
    console.log(error.message);
  } else if (error instanceof FlxblApiError) {
    // Other API errors (403, 429, 500, etc.)
    console.log(error.statusCode, error.message);
  }
}

Code Generation

The SDK works best with a generated typed client. Use the FLXBL CLI to generate TypeScript interfaces and a typed client factory from your active schema:

# Generate a typed client
npm install -g @flxbl-dev/cli
flxbl generate

The generated client includes entity types, create/update input types, query option helpers, and a createFlxblClient() factory with typed collection accessors for every entity in your schema.

Next Steps

  • CLI - Generate typed clients, manage schemas, and query data
  • API Reference - Complete REST and GraphQL documentation
  • Query DSL - Deep dive into filter operators and traversals
  • Schema Design - Define entities, fields, and relationships