Core Concepts
Create Builder

createBuilder

What Does It Do?

createBuilder is the function that transforms your plain register object into a smart builder object with superpowers. It adds special methods ($use and $get) at every level, making key generation automatic and type-safe.

import { createBuilder } from '@ibnlanre/builder';

Think of it as: Regular object → Builder object = Regular object + Smart key generation

Basic Usage

The simplest way to use createBuilder:

const register = {
  users: {
    list: () => fetch('/api/users').then(r => r.json()),
    detail: (id: number) => fetch(`/api/users/${id}`).then(r => r.json())
  }
};
 
// Transform it
const api = createBuilder(register);
 
// Now you have both:
// 1. Original functions
api.$use.users.detail(5);  // Calls fetch('/api/users/5')
 
// 2. Automatic key generation
api.users.detail.$use(5);  // ['users', 'detail', 5]

What changed? The structure is identical, but now every node has $use and $get methods for generating keys.

Without Prefixes

By default, keys start from the root of your structure:

const store = createBuilder({
  products: {
    list: () => { /* ... */ },
    detail: (id: number) => { /* ... */ }
  },
  cart: {
    items: () => { /* ... */ }
  }
});
 
// Generated keys start from your structure
store.products.list.$use();        // ['products', 'list']
store.cart.items.$use();           // ['cart', 'items']
store.$get();                      // [] - empty at root

With Prefixes

Prefixes add a namespace to all your keys. This is useful for:

  • Versioning your cache (['v1', ...]['v2', ...])
  • Separating concerns (['api', ...] vs ['ui', ...])
  • Avoiding collisions when using multiple builders
const api = createBuilder(register, {
  prefix: ['api', 'v2']
});
 
// Now all keys are prefixed
api.users.list.$use();        // ['api', 'v2', 'users', 'list']
api.users.detail.$use(5);     // ['api', 'v2', 'users', 'detail', 5]
api.$get();                   // ['api', 'v2'] - just the prefix

Real-World Examples

Example 1: API Client with Versioning

// Version 2 of your API
const apiV2 = createBuilder({
  users: {
    list: async (filter?: string) => {
      const url = filter ? `/api/v2/users?filter=${filter}` : '/api/v2/users';
      return fetch(url).then(r => r.json());
    },
    detail: async (id: number) => {
      return fetch(`/api/v2/users/${id}`).then(r => r.json());
    }
  }
}, { prefix: ['api', 'v2'] });
 
// Usage
apiV2.users.list.$use('active');  // ['api', 'v2', 'users', 'list', 'active']
 
// When you upgrade to v3, just change the prefix
const apiV3 = createBuilder(sameRegister, { prefix: ['api', 'v3'] });

Example 2: Multiple Builders in One App

// API calls
const api = createBuilder({
  posts: { list, detail, create },
  comments: { list, create }
}, { prefix: ['api'] });
 
// UI state
const ui = createBuilder({
  modals: {
    isLoginOpen: false,
    isCartOpen: false
  },
  theme: 'dark'
}, { prefix: ['ui'] });
 
// Settings
const settings = createBuilder({
  notifications: true,
  language: 'en'
}, { prefix: ['settings'] });
 
// All keys are namespaced:
api.posts.list.$use();           // ['api', 'posts', 'list']
ui.modals.isLoginOpen.$use();    // ['ui', 'modals', 'isLoginOpen']
settings.language.$use();        // ['settings', 'language']

Example 3: Feature Flags

const features = createBuilder({
  payments: {
    stripe: true,
    paypal: false
  },
  ui: {
    darkMode: true,
    betaFeatures: false
  }
}, { prefix: ['features'] });
 
// Check feature flags
features.$use.payments.stripe;           // true
features.payments.stripe.$use();         // ['features', 'payments', 'stripe']

How Prefixes Work

Prefixes are prepended to every generated key:

const builder = createBuilder(register, { prefix: ['app', 'cache'] });
 
// Every key starts with the prefix
builder.users.list.$use();        // ['app', 'cache', 'users', 'list']
builder.products.detail.$use(5);  // ['app', 'cache', 'products', 'detail', 5]
 
// The root $get returns just the prefix
builder.$get();                   // ['app', 'cache']

Prefix Guidelines

Keep prefixes short: Use 1-3 segments maximum. Long prefixes make keys harder to read and debug.

// ✅ Good prefixes
{ prefix: ['api'] }
{ prefix: ['v2'] }
{ prefix: ['app', 'cache'] }
 
// ❌ Too verbose
{ prefix: ['my', 'application', 'production', 'api', 'version', '2'] }

TypeScript Support

createBuilder preserves your types automatically:

const api = createBuilder({
  users: {
    detail: async (id: number) => {
      return fetch(`/api/users/${id}`).then(r => r.json());
    }
  }
});
 
// TypeScript knows the structure
api.users.detail.$use(123);    // ✅ Works
api.users.detail.$use('abc');  // ❌ Error: Expected number
 
// Autocomplete works too
api.users.  // Your IDE suggests: detail, $use, $get

Common Patterns

Pattern 1: Environment-Based Prefixes

const isDev = process.env.NODE_ENV === 'development';
 
const api = createBuilder(register, {
  prefix: isDev ? ['dev', 'api'] : ['prod', 'api']
});
 
// Dev: ['dev', 'api', 'users', 'list']
// Prod: ['prod', 'api', 'users', 'list']

Pattern 2: User-Scoped Keys

function createUserApi(userId: number) {
  return createBuilder(register, {
    prefix: ['user', userId]
  });
}
 
const user123Api = createUserApi(123);
user123Api.posts.list.$use();  // ['user', 123, 'posts', 'list']

Pattern 3: Multi-Tenant Apps

function createTenantApi(tenantId: string) {
  return createBuilder(register, {
    prefix: ['tenant', tenantId]
  });
}
 
const acmeApi = createTenantApi('acme-corp');
acmeApi.invoices.list.$use();  // ['tenant', 'acme-corp', 'invoices', 'list']

When to Use Prefixes

ScenarioUse Prefix?Example
Single app with one APIOptional{ prefix: ['api'] }
Multiple API versions✅ Yes{ prefix: ['v2'] }
Separate UI/API state✅ Yes{ prefix: ['ui'] } or { prefix: ['api'] }
User-specific data✅ Yes{ prefix: ['user', userId] }
Simple key generation❌ Not neededNo prefix

Comparison

const register = {
  posts: {
    list: () => { /* ... */ }
  }
};
 
// Without prefix
const builder1 = createBuilder(register);
builder1.posts.list.$use();  // ['posts', 'list']
 
// With single prefix
const builder2 = createBuilder(register, { prefix: ['api'] });
builder2.posts.list.$use();  // ['api', 'posts', 'list']
 
// With multiple prefixes
const builder3 = createBuilder(register, { prefix: ['api', 'v2'] });
builder3.posts.list.$use();  // ['api', 'v2', 'posts', 'list']

Summary

createBuilder is your gateway to Builder's features:

  • Transforms your register into a builder object
  • Prefixes (optional) namespace all your keys
  • Type-safe - preserves your TypeScript types
  • Zero config - works with just a register

The formula:

Register + createBuilder = Builder with $use and $get methods
Register + Prefix = Namespaced keys

Next Steps