Core Concepts
Overview

Core Concepts

Understanding how Builder works will help you use it effectively. Don't worry, it's simpler than it looks!

The Three Key Pieces

Builder revolves around three main concepts:

1. Register - Your Data Structure

A plain JavaScript object containing your keys, values, and functions:

const register = {
  users: {
    list: () => fetch('/api/users'),
    detail: (id: number) => fetch(`/api/users/${id}`)
  }
};

Think of it as: Your table of contents or API map.

2. createBuilder - The Transformer

The function that transforms your register into a builder:

import { createBuilder } from '@ibnlanre/builder';
 
const api = createBuilder(register);

Think of it as: The magic spell that adds superpowers to your object.

3. Builder - The Enhanced Object

The result: your register with special $use and $get methods added:

// Access values (same as before)
api.$use.users.detail(5);
 
// Generate keys (the superpower!)
api.users.detail.$use(5);  // ['users', 'detail', 5]

Think of it as: Your register, but with automatic key generation.

How They Work Together

// 1. Define your structure (Register)
const register = {
  products: {
    list: () => fetch('/api/products')
  }
};
 
// 2. Transform it (createBuilder)
const store = createBuilder(register);
 
// 3. Use it (Builder)
store.products.list.$use();       // ['products', 'list'] - The key
await store.$use.products.list(); // Calls the API - The value

Framework Agnostic

Builder works with any JavaScript framework or library:

// React + TanStack Query
import { useQuery } from '@tanstack/react-query';
const { data } = useQuery({
  queryKey: api.posts.list.$use(),
  queryFn: api.$use.posts.list
});
 
// Vue + VueQuery
import { useQuery } from '@tanstack/vue-query';
const { data } = useQuery({
  queryKey: api.posts.list.$use(),
  queryFn: api.$use.posts.list
});
 
// Svelte + SWR
import useSWR from 'swr';
const { data } = useSWR(api.$get('posts.list'), api.$use.posts.list);
 
// Vanilla JavaScript
const cacheKey = api.products.detail.$use(123).join('.');
localStorage.setItem(cacheKey, JSON.stringify(data));

Builder doesn't care what framework you use. It's just JavaScript objects and arrays.

TypeScript: Optional but Recommended

Builder is written in TypeScript, but you don't have to use it:

Without TypeScript (JavaScript)

const api = createBuilder({
  users: {
    list: () => fetch('/api/users').then(r => r.json())
  }
});
 
// Works fine, but no autocomplete
api.users.list.$use();

With TypeScript

const api = createBuilder({
  users: {
    list: async (): Promise<User[]> => {
      return fetch('/api/users').then(r => r.json());
    },
    detail: async (id: number): Promise<User> => {
      return fetch(`/api/users/${id}`).then(r => r.json());
    }
  }
});
 
// ✅ Autocomplete suggests: users, $use, $get
api.users.
 
// ✅ TypeScript enforces correct parameters
api.users.detail.$use(123);    // Works
api.users.detail.$use('abc');  // Error!
 
// ✅ Return types are inferred
const user: User = await api.$use.users.detail(1);

Benefits of TypeScript:

  • Autocomplete for all keys
  • Compile-time error checking
  • Better IntelliSense documentation
  • Safer refactoring

Quick Reference

ConceptWhat It IsExample
RegisterYour plain object{ users: { list: () => {...} } }
createBuilderTransformer functioncreateBuilder(register)
BuilderEnhanced objectapi.users.list.$use()
$useAccess values (root)
Generate keys (nodes)
api.$use.users.list()
api.users.list.$use()
$getFlexible key generationapi.users.list.$get('extra', 'params')
PrefixNamespace for all keyscreateBuilder(register, { prefix: ['v2'] })

Practical Workflow

Here's how you'd typically use Builder in a project:

  1. Define your register (one time, in one file)
// api/index.ts
const register = {
  users: { /* ... */ },
  posts: { /* ... */ },
  comments: { /* ... */ }
};
 
export const api = createBuilder(register);
  1. Import where needed
// components/UserList.tsx
import { api } from '../api';
  1. Use for keys and values
const { data } = useQuery({
  queryKey: api.users.list.$use(),
  queryFn: api.$use.users.list
});
  1. Refactor easily
const register = {
  accounts: { /* ... */ }, // Changed 'users' to 'accounts'?
};
 
// Update once in register, everywhere updates automatically
 

Next Steps

Ready to dive deeper? Learn about each piece:

Or jump straight to practical examples: