Skip to content

TypeScript Usage

This guide covers TypeScript usage with Flowfull Client.

Type Safety

Flowfull Client is written in TypeScript and includes full type definitions out of the box. No additional @types packages are needed.

Typed API Calls

Use TypeScript generics to type your API responses:

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

const api = createFlowfull('https://api.example.com');

// Define your types
interface User {
  id: string;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'guest';
  createdAt: string;
}

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

const user = await api.get<User>('/users/123');
// user is typed as User

Typed POST Requests

typescript
interface CreateUserInput {
  name: string;
  email: string;
  password: string;
  role?: 'admin' | 'user';
}

interface CreateUserResponse {
  id: string;
  name: string;
  email: string;
  role: string;
  createdAt: string;
}

const newUser = await api.post<CreateUserResponse>(
  '/users',
  {
    name: 'John Doe',
    email: 'john@example.com',
    password: 'password123',
    role: 'user'
  } as CreateUserInput
);

// newUser is typed as CreateUserResponse
console.log(newUser.id);

Typed Query Builder

typescript
interface User {
  id: string;
  name: string;
  email: string;
  status: 'active' | 'inactive';
  role: 'admin' | 'user' | 'guest';
}

const users = await api.query<User>('/users')
  .filter('status', 'active')
  .filter('role', 'admin')
  .orderBy('name', 'asc')
  .limit(10)
  .execute();

// users is typed as User[]

Type Inference

Flowfull Client provides excellent type inference:

typescript
// Type is inferred from the generic
const user = await api.get<User>('/users/123');

// TypeScript knows user has these properties
console.log(user.name);    // ✅ OK
console.log(user.email);   // ✅ OK
console.log(user.invalid); // ❌ Error: Property 'invalid' does not exist

Error Handling with Types

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

try {
  const users = await api.get<User[]>('/users');
  console.log(users);
} catch (error) {
  if (error instanceof FlowfullError) {
    console.error('Status:', error.status);
    console.error('Message:', error.message);
    console.error('Data:', error.data);
  } else {
    console.error('Unknown error:', error);
  }
}

Generic Components

Create reusable components with generics:

typescript
import { useState, useEffect } from 'react';
import { api } from '../lib/api';

interface ListProps<T> {
  endpoint: string;
  renderItem: (item: T) => React.ReactNode;
}

function List<T extends { id: string }>({ endpoint, renderItem }: ListProps<T>) {
  const [items, setItems] = useState<T[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchItems() {
      try {
        const data = await api.get<T[]>(endpoint);
        setItems(data);
      } catch (error) {
        console.error('Error fetching items:', error);
      } finally {
        setLoading(false);
      }
    }
    
    fetchItems();
  }, [endpoint]);

  if (loading) return <div>Loading...</div>;

  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

// Usage
interface User {
  id: string;
  name: string;
  email: string;
}

function UserList() {
  return (
    <List<User>
      endpoint="/users"
      renderItem={(user) => (
        <div>
          <strong>{user.name}</strong>
          <span>{user.email}</span>
        </div>
      )}
    />
  );
}

Custom Hooks with Types

typescript
import { useState, useEffect } from 'react';
import { api } from '../lib/api';

function useApi<T>(endpoint: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    async function fetchData() {
      try {
        const result = await api.get<T>(endpoint);
        setData(result);
      } catch (err: any) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }
    
    fetchData();
  }, [endpoint]);

  return { data, loading, error };
}

// Usage
interface User {
  id: string;
  name: string;
  email: string;
}

function UserProfile({ userId }: { userId: string }) {
  const { data: user, loading, error } = useApi<User>(`/users/${userId}`);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!user) return <div>User not found</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

Strict Type Checking

Enable strict type checking in your tsconfig.json:

json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true
  }
}

Next Steps