Skip to content

How to Use It ​

This guide covers the essential patterns and common scenarios for using @pubflow/flowfull-client in your applications.

Installation ​

First, install the package:

bash
npm install @pubflow/flowfull-client
bash
yarn add @pubflow/flowfull-client
bash
pnpm add @pubflow/flowfull-client
bash
bun add @pubflow/flowfull-client

Basic Setup ​

Creating a Client ​

The simplest way to create a client is using the createFlowfull factory function:

typescript
import { createFlowfull } from '@pubflow/flowfull-client';

// Basic setup
const api = createFlowfull('https://api.myapp.com');

With Configuration ​

You can customize the client with various options:

typescript
const api = createFlowfull('https://api.myapp.com', {
  // Session management
  sessionId: 'your-session-id',           // Manual session
  includeSession: true,                    // Include session in requests (default: true)
  sessionHeader: 'X-Session-Id',          // Custom session header (default: 'X-Session-Id')
  
  // Custom headers
  headers: {
    'X-API-Version': 'v2',
    'X-Client-ID': 'web-app'
  },
  
  // Retry configuration
  retryAttempts: 3,                       // Number of retry attempts (default: 3)
  
  // Timeout
  timeout: 30000,                         // Request timeout in ms (default: 30000)
  
  // Callbacks
  onRequest: async (config) => {
    console.log('Request:', config);
  },
  onResponse: async (response) => {
    console.log('Response:', response);
  },
  onError: (error) => {
    console.error('Error:', error);
  }
});

Making Requests ​

Simple HTTP Methods ​

GET Request ​

typescript
// Simple GET
const response = await api.get('/users');

// GET with query parameters
const response = await api.get('/users', {
  params: {
    page: 1,
    limit: 20
  }
});

// GET with custom headers
const response = await api.get('/users', {
  headers: {
    'X-Custom-Header': 'value'
  }
});

POST Request ​

typescript
// Create a new user
const newUser = {
  name: 'John Doe',
  email: 'john@example.com',
  age: 30
};

const response = await api.post('/users', newUser);

if (response.success) {
  console.log('Created user:', response.data);
} else {
  console.error('Error:', response.error);
}

PUT Request ​

typescript
// Update entire resource
const updates = {
  name: 'John Updated',
  email: 'john.updated@example.com',
  age: 31
};

const response = await api.put('/users/123', updates);

PATCH Request ​

typescript
// Partial update
const response = await api.patch('/users/123', {
  status: 'active'
});

DELETE Request ​

typescript
// Delete a resource
const response = await api.delete('/users/123');

if (response.success) {
  console.log('User deleted successfully');
}

Using the Query Builder ​

The query builder provides a powerful, chainable API for building complex queries.

Basic Filtering ​

typescript
// Simple equality filter
const response = await api
  .query('/users')
  .where('status', 'active')
  .get();

// Multiple filters
const response = await api
  .query('/products')
  .where('status', 'active')
  .where('price', 'gte', 50)
  .where('price', 'lte', 200)
  .get();

Using Filter Operators ​

Import and use the 14 filter operators:

typescript
import { 
  eq, ne, gt, gte, lt, lte,           // Comparison
  like, ilike, startsWith, endsWith,  // String
  inOp, notIn,                        // Array
  isNull, isNotNull,                  // Null
  between, notBetween                 // Range
} from '@pubflow/flowfull-client';

// Comparison operators
const adults = await api
  .query('/users')
  .where('age', gte(18))
  .get();

// String operators
const gmailUsers = await api
  .query('/users')
  .where('email', endsWith('@gmail.com'))
  .get();

// Array operators
const activeUsers = await api
  .query('/users')
  .where('status', inOp(['active', 'verified', 'premium']))
  .get();

// Null operators
const verifiedUsers = await api
  .query('/users')
  .where('verified_at', isNotNull())
  .get();

// Range operators
const midRangeProducts = await api
  .query('/products')
  .where('price', between(50, 200))
  .get();

Add full-text search to your queries:

typescript
// Simple search
const results = await api
  .query('/products')
  .search('laptop')
  .get();

// Search with filters
const results = await api
  .query('/products')
  .search('laptop')
  .where('category', 'electronics')
  .where('price', lte(1000))
  .get();

// Using shorthand 'q()'
const results = await api
  .query('/products')
  .q('laptop')
  .get();

Pagination ​

typescript
// Page-based pagination
const response = await api
  .query('/users')
  .page(2)
  .limit(25)
  .get();

// Access pagination metadata
if (response.success && response.meta) {
  console.log(`Page ${response.meta.page} of ${response.meta.totalPages}`);
  console.log(`Total items: ${response.meta.total}`);
}

// Offset-based pagination
const response = await api
  .query('/users')
  .offset(50)
  .limit(25)
  .get();

Sorting ​

typescript
// Single sort
const response = await api
  .query('/users')
  .sort('name', 'asc')
  .get();

// Multiple sorts
const response = await api
  .query('/users')
  .sort('status', 'asc')
  .sort('name', 'asc')
  .get();

// Using orderBy alias
const response = await api
  .query('/users')
  .orderBy('created_at', 'desc')
  .get();

Custom Parameters ​

Add custom query parameters:

typescript
// Single parameter
const response = await api
  .query('/users')
  .param('include', 'profile')
  .get();

// Multiple parameters
const response = await api
  .query('/users')
  .params({
    include: 'profile,settings',
    fields: 'id,name,email',
    expand: 'organization'
  })
  .get();

Custom Headers ​

Add custom headers to specific requests:

typescript
// Single header
const response = await api
  .query('/users')
  .header('X-API-Version', 'v2')
  .get();

// Multiple headers
const response = await api
  .query('/users')
  .headers({
    'X-API-Version': 'v2',
    'X-Client-ID': 'web-app'
  })
  .get();

Complex Queries ​

Combine everything for powerful queries:

typescript
const response = await api
  .query('/products')
  .search('laptop')                           // Full-text search
  .where('category', 'electronics')           // Category filter
  .where('price', between(500, 2000))         // Price range
  .where('rating', gte(4))                    // Minimum rating
  .where('stock', gt(0))                      // In stock
  .where('status', inOp(['active', 'new']))   // Status filter
  .sort('price', 'asc')                       // Sort by price
  .page(1)                                    // First page
  .limit(20)                                  // 20 items
  .param('include', 'images,reviews')         // Include relations
  .get();

Session Management ​

The client automatically detects sessions from storage:

typescript
// Browser: Searches localStorage for common session keys
// React Native: Searches AsyncStorage for common session keys
const api = createFlowfull('https://api.myapp.com');

// Common session keys searched:
// - pubflow_session_id
// - session_id
// - sessionId
// - X-Session-ID
// - x-session-id

Manual Session ​

Provide a session ID directly:

typescript
const api = createFlowfull('https://api.myapp.com', {
  sessionId: 'your-session-id-here'
});

Dynamic Session ​

Use a function to retrieve the session dynamically:

typescript
const api = createFlowfull('https://api.myapp.com', {
  getSessionId: async () => {
    // Get from secure storage
    return await SecureStore.getItemAsync('session_id');
  }
});

Custom Storage ​

Provide a custom storage adapter:

typescript
const api = createFlowfull('https://api.myapp.com', {
  storage: {
    getItem: async (key) => {
      return await myCustomStorage.get(key);
    },
    setItem: async (key, value) => {
      await myCustomStorage.set(key, value);
    },
    removeItem: async (key) => {
      await myCustomStorage.delete(key);
    }
  }
});

Sessionless Requests ​

For public APIs or login endpoints:

typescript
// Disable sessions globally
const api = createFlowfull('https://api.myapp.com', {
  includeSession: false
});

// Or per-request
const response = await api.get('/public/data', {
  includeSession: false
});

Session Methods ​

Manage sessions programmatically:

typescript
// Get current session
const sessionId = await api.getSessionId();

// Set session
api.setSessionId('new-session-id');

// Clear session
api.clearSession();

// Check if session exists
const hasSession = await api.hasSession();

Response Handling ​

Standard Response Format ​

All responses follow a consistent format:

typescript
interface ApiResponse<T> {
  success: boolean;      // true if HTTP 2xx
  data?: T;              // Response data
  error?: string;        // Error message (if failed)
  message?: string;      // Additional message
  status: number;        // HTTP status code
  meta?: {               // Pagination metadata
    page?: number;
    limit?: number;
    total?: number;
    totalPages?: number;
  };
}

Handling Success ​

typescript
const response = await api.get('/users');

if (response.success) {
  console.log('Data:', response.data);

  // Check for pagination
  if (response.meta) {
    console.log(`Page ${response.meta.page} of ${response.meta.totalPages}`);
    console.log(`Total items: ${response.meta.total}`);
  }
}

Handling Errors ​

typescript
const response = await api.post('/users', userData);

if (!response.success) {
  console.error(`Error ${response.status}: ${response.error}`);

  // Handle specific error codes
  switch (response.status) {
    case 400:
      console.error('Bad request:', response.error);
      break;
    case 401:
      console.error('Unauthorized - please login');
      break;
    case 403:
      console.error('Forbidden - insufficient permissions');
      break;
    case 404:
      console.error('Not found');
      break;
    case 500:
      console.error('Server error');
      break;
  }
}

TypeScript Type Safety ​

typescript
interface User {
  id: string;
  name: string;
  email: string;
}

// Type the response
const response = await api.get<User[]>('/users');

if (response.success) {
  // TypeScript knows response.data is User[]
  response.data.forEach(user => {
    console.log(user.name);  // ✅ Autocomplete works
  });
}

Advanced Features ​

Interceptors ​

Modify requests and responses globally:

typescript
// Request interceptor
api.addRequestInterceptor(async (config) => {
  // Add timestamp to all requests
  config.headers['X-Request-Time'] = new Date().toISOString();

  // Add device info
  config.headers['X-Device-ID'] = await getDeviceId();

  // Log request
  console.log(`[${config.method}] ${config.url}`);

  return config;
});

// Response interceptor
api.addResponseInterceptor(async (response) => {
  // Log all errors
  if (!response.success) {
    console.error('API Error:', response.error);
  }

  // Transform data
  if (response.success && response.data) {
    // Convert dates
    if (Array.isArray(response.data)) {
      response.data = response.data.map(item => ({
        ...item,
        created_at: new Date(item.created_at)
      }));
    }
  }

  return response;
});

Custom Headers ​

Set default headers for all requests:

typescript
// Set header
api.setHeader('X-API-Key', 'your-api-key');
api.setHeader('X-Client-Version', '1.0.0');

// Remove header
api.removeHeader('X-API-Key');

Timeout Configuration ​

typescript
// Global timeout
const api = createFlowfull('https://api.myapp.com', {
  timeout: 30000  // 30 seconds
});

// Per-request timeout
const response = await api.get('/users', {
  timeout: 5000  // 5 seconds
});

Abort Requests ​

typescript
// Create abort controller
const controller = new AbortController();

// Make request with signal
const promise = api.get('/users', {
  signal: controller.signal
});

// Abort after 5 seconds
setTimeout(() => {
  controller.abort();
}, 5000);

try {
  const response = await promise;
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Request aborted');
  }
}

Platform-Specific Usage ​

Browser / React ​

typescript
import { createFlowfull } from '@pubflow/flowfull-client';

// Auto-detects localStorage
const api = createFlowfull('https://api.myapp.com');

// Use in React component
function UserList() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    const fetchUsers = async () => {
      const response = await api.get('/users');
      if (response.success) {
        setUsers(response.data);
      }
    };

    fetchUsers();
  }, []);

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

React Native / Expo ​

typescript
import { createFlowfull } from '@pubflow/flowfull-client';
import AsyncStorage from '@react-native-async-storage/async-storage';

// Auto-detects AsyncStorage
const api = createFlowfull('https://api.myapp.com');

// Use in React Native component
function UserList() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    const fetchUsers = async () => {
      const response = await api.get('/users');
      if (response.success) {
        setUsers(response.data);
      }
    };

    fetchUsers();
  }, []);

  return (
    <FlatList
      data={users}
      renderItem={({ item }) => <Text>{item.name}</Text>}
      keyExtractor={item => item.id}
    />
  );
}

Node.js / Bun ​

typescript
import { createFlowfull } from '@pubflow/flowfull-client';

// Manual session management
const api = createFlowfull('https://api.myapp.com', {
  sessionId: process.env.SESSION_ID
});

// Use in server
async function getUsers() {
  const response = await api.get('/users');
  return response.data;
}

Common Patterns ​

Login Flow ​

typescript
// Create client without session
const api = createFlowfull('https://api.myapp.com', {
  includeSession: false
});

// Login
const loginResponse = await api.post('/auth/login', {
  email: 'user@example.com',
  password: 'password123'
}, {
  includeSession: false  // Don't include session for login
});

if (loginResponse.success) {
  // Set session after successful login
  api.setSessionId(loginResponse.data.session_id);

  // Now all requests include session
  const profile = await api.get('/profile');
}

Logout Flow ​

typescript
// Logout
await api.post('/auth/logout');

// Clear session
api.clearSession();

Pagination Loop ​

typescript
async function getAllUsers() {
  const allUsers = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await api
      .query('/users')
      .page(page)
      .limit(100)
      .get();

    if (response.success) {
      allUsers.push(...response.data);
      hasMore = response.meta && response.meta.page < response.meta.totalPages;
      page++;
    } else {
      hasMore = false;
    }
  }

  return allUsers;
}

Error Retry ​

typescript
async function fetchWithRetry(endpoint, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await api.get(endpoint);

    if (response.success) {
      return response;
    }

    // Wait before retry (exponential backoff)
    if (i < maxRetries - 1) {
      await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
    }
  }

  throw new Error('Max retries exceeded');
}

Next Steps ​