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 full CRUD operations. Each collection is typed as createCollection<T, TCreate, TUpdate>() with methods:

Method Description
findMany(options?) Query records with filters, sorting, pagination, and traversals
findById(id) Get a single record by ID
create(data) Create a new record
update(id, data) Partial update of a record
delete(id) Delete a record by ID
// With the generated typed client (from `flxbl generate`):
import { createFlxblClient } from './flxbl/_generated';

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

// Typed CRUD operations
const products = await client.Product.findMany({
  where: { inStock: { $eq: true } },
  orderBy: 'price',
  orderDirection: 'DESC',
  limit: 20,
});

const product = await client.Product.findById('node_abc123');

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

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

await client.Product.delete('node_abc123');

Query Options

The findMany method accepts a QueryOptions<T> object with the following fields:

Field Type Description
where object Filter conditions using query operators
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 client.Product.findMany({
  // Filter conditions
  where: {
    price: { $lte: 100 },
    inStock: { $eq: true },
    tags: { $contains: 'electronics' },
  },

  // 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 client.vectorSearch(). This requires a VECTOR field on the target entity.

// Semantic search using vector embeddings
const results = await client.vectorSearch<Product>('Product', {
  field: 'embedding',
  vector: [0.1, 0.2, 0.3, /* ...1536 dimensions */],
  topK: 5,
  where: { isActive: { $eq: true } },
  select: ['name', 'price', 'description'],
});

for (const result of results) {
  console.log(result.data.name);   // Product name
  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('Product', 'node_abc123');

// Create a relationship
await rel.create('BELONGS_TO', 'node_category_xyz', {
  addedAt: new Date().toISOString(),
});

// List relationships
const related = await rel.list('BELONGS_TO', 'out');
console.log(related.items);

// Delete a relationship
await rel.delete('BELONGS_TO', 'node_category_xyz');

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 client.Product.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