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: '[email protected]',
    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 ​