@buildbase/sdk 0.0.28 → 0.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # @buildbase/sdk
2
2
 
3
- A React SDK for [BuildBase](https://www.buildbase.app/) that provides essential components to build SaaS applications faster. Skip the plumbing and focus on your core product with built-in authentication, workspace management, and user management.
3
+ A React SDK for [BuildBase](https://www.buildbase.app/) that provides essential components to build SaaS applications faster. Skip the plumbing and focus on your core product with built-in authentication, workspace management, billing, and more.
4
+
5
+ Also works server-side (Next.js API routes, Express, Hono) — see [Server-Side Usage](#server-side-usage) below.
4
6
 
5
7
  ## 📑 Table of Contents
6
8
 
7
9
  - [Features](#-features)
8
- - [Installation](#-installation)
9
- - [Quick Start](#-quick-start)
10
+ - [Installation](#installation)
11
+ - [Quick Start](#quick-start)
10
12
  - [Authentication](#-authentication)
11
13
  - [Role-Based Access Control](#-role-based-access-control)
12
14
  - [Feature Flags](#️-feature-flags)
@@ -28,7 +30,7 @@ A React SDK for [BuildBase](https://www.buildbase.app/) that provides essential
28
30
  - [Troubleshooting](#-troubleshooting)
29
31
  - [API Reference](#-api-reference)
30
32
  - [Best Practices](#-best-practices)
31
- - [Documentation](#further-documentation)
33
+ - [Server-Side Usage](#server-side-usage)
32
34
 
33
35
  ## 🚀 Features
34
36
 
@@ -47,99 +49,103 @@ A React SDK for [BuildBase](https://www.buildbase.app/) that provides essential
47
49
  - **📝 Beta Form** - Pre-built signup/waitlist form component
48
50
  - **📡 Event System** - Subscribe to user and workspace events
49
51
  - **🛡️ Error Handling** - Centralized error handling with error boundaries
52
+ - **🖥️ Server-Side SDK** - `BuildBase()` factory for API routes, background jobs, Express, Hono — zero React dependency
50
53
 
51
- ## 📦 Installation
52
-
53
- ```bash
54
- npm install @buildbase/sdk
55
- ```
56
-
57
- ### Peer Dependencies
58
-
59
- This package requires React 19 and React DOM 19:
54
+ ## Installation
60
55
 
61
56
  ```bash
62
- npm install react@^19.0.0 react-dom@^19.0.0
57
+ npm install @buildbase/sdk react@^19.0.0 react-dom@^19.0.0
63
58
  ```
64
59
 
65
- ## 🏗️ Quick Start
60
+ ## Quick Start
66
61
 
67
62
  ### 1. Import CSS
68
63
 
69
- First, import the required CSS file in your app:
70
-
71
64
  ```tsx
72
- import '@buildbase/sdk/dist/saas-os.css';
65
+ // app/layout.tsx (or your root layout)
66
+ import '@buildbase/sdk/css'
73
67
  ```
74
68
 
75
- ### 2. Create Client Provider
76
-
77
- Create a client-side provider component:
69
+ ### 2. Create Provider
78
70
 
79
71
  ```tsx
80
- 'use client';
72
+ // components/provider.tsx
73
+ 'use client'
81
74
 
82
- import { SaaSOSProvider } from '@buildbase/sdk';
83
- import React from 'react';
75
+ import { SaaSOSProvider } from '@buildbase/sdk/react'
76
+ import { ApiVersion } from '@buildbase/sdk'
84
77
 
85
- export default function SaaSProvider(props: { children: React.ReactNode }) {
78
+ export default function SaaSProvider({ children }: { children: React.ReactNode }) {
86
79
  return (
87
80
  <SaaSOSProvider
88
81
  serverUrl="https://your-api-server.com"
89
- version="v1"
82
+ version={ApiVersion.V1}
90
83
  orgId="your-org-id"
91
84
  auth={{
92
85
  clientId: 'your-client-id',
93
86
  redirectUrl: 'http://localhost:3000',
94
87
  callbacks: {
95
- handleAuthentication: async (code: string) => {
96
- // Exchange OAuth code for session ID
97
- const response = await fetch('/api/auth/token', {
88
+ // Called on page refresh to restore session from httpOnly cookie
89
+ getSession: async () => {
90
+ const res = await fetch('/api/auth/session')
91
+ const data = await res.json()
92
+ return data.sessionId ?? null
93
+ },
94
+
95
+ // Called after OAuth redirect to exchange code for sessionId
96
+ handleAuthentication: async (code) => {
97
+ const res = await fetch('/api/auth/verify', {
98
98
  method: 'POST',
99
+ headers: { 'Content-Type': 'application/json' },
99
100
  body: JSON.stringify({ code }),
100
- });
101
- const data = await response.json();
102
- // Return sessionId - SDK will use this for authenticated requests
103
- return { sessionId: data.sessionId };
101
+ })
102
+ const data = await res.json()
103
+ return { sessionId: data.sessionId }
104
104
  },
105
+
106
+ // Called on sign out to clear the httpOnly cookie
105
107
  onSignOut: async () => {
106
- // Clean up any custom tokens/storage on sign out
107
- localStorage.removeItem('custom_token');
108
+ await fetch('/api/auth/signout', { method: 'POST' })
109
+ window.location.reload()
108
110
  },
109
- handleEvent: async (eventType, data) => {
110
- // Handle SDK events (user created, workspace changed, etc.)
111
- console.log('SDK Event:', eventType, data);
111
+
112
+ handleEvent: (eventType, data) => {
113
+ console.log('SDK Event:', eventType, data)
112
114
  },
113
115
  onWorkspaceChange: async ({ workspace, user, role }) => {
114
- // Called before switching workspace (e.g. generate token). Used on "Switch to" and page refresh/restore.
115
- // Switch proceeds only when this resolves; reject to abort.
116
- console.log('Switching to workspace:', workspace.name, 'as', role);
116
+ console.log('Switching to:', workspace.name, 'as', role)
117
117
  },
118
118
  },
119
119
  }}
120
120
  >
121
- {props.children}
121
+ {children}
122
122
  </SaaSOSProvider>
123
- );
123
+ )
124
124
  }
125
125
  ```
126
126
 
127
- ### 3. Wrap Your App
127
+ The SDK uses the same session pattern as next-auth: the session token lives in an httpOnly cookie (set by your server), and the SDK calls `getSession()` on page refresh to restore it. You need three server endpoints:
128
+
129
+ - **`/api/auth/verify`** — exchanges OAuth code for sessionId, sets httpOnly cookie
130
+ - **`/api/auth/session`** — reads httpOnly cookie, returns `{ sessionId }` (called on page refresh)
131
+ - **`/api/auth/signout`** — clears the httpOnly cookie
128
132
 
129
- Use the provider in your app layout:
133
+ ### 3. Wrap Your App
130
134
 
131
135
  ```tsx
132
- import SaaSProvider from './components/SaaSProvider';
136
+ // app/layout.tsx
137
+ import SaaSProvider from '@/components/provider'
138
+ import '@buildbase/sdk/css'
133
139
 
134
- function App() {
140
+ export default function RootLayout({ children }) {
135
141
  return (
136
- <SaaSProvider>
137
- <YourAppContent />
138
- </SaaSProvider>
139
- );
142
+ <html>
143
+ <body>
144
+ <SaaSProvider>{children}</SaaSProvider>
145
+ </body>
146
+ </html>
147
+ )
140
148
  }
141
-
142
- export default App;
143
149
  ```
144
150
 
145
151
  ### 4. Workspace Management
@@ -148,7 +154,7 @@ The WorkspaceSwitcher component uses a render prop pattern, giving you full cont
148
154
 
149
155
  ```tsx
150
156
  import React from 'react';
151
- import { WorkspaceSwitcher } from '@buildbase/sdk';
157
+ import { WorkspaceSwitcher } from '@buildbase/sdk/react';
152
158
 
153
159
  function WorkspaceExample() {
154
160
  return (
@@ -192,7 +198,7 @@ function WorkspaceExample() {
192
198
  Use the `useSaaSAuth` hook to manage authentication state and actions:
193
199
 
194
200
  ```tsx
195
- import { useSaaSAuth } from '@buildbase/sdk';
201
+ import { useSaaSAuth } from '@buildbase/sdk/react';
196
202
 
197
203
  function AuthExample() {
198
204
  const { user, isAuthenticated, signIn, signOut, status } = useSaaSAuth();
@@ -254,7 +260,7 @@ openWorkspaceSettings('danger'); // Delete workspace (owner only)
254
260
  For declarative rendering, use the conditional components:
255
261
 
256
262
  ```tsx
257
- import { WhenAuthenticated, WhenUnauthenticated } from '@buildbase/sdk';
263
+ import { WhenAuthenticated, WhenUnauthenticated } from '@buildbase/sdk/react';
258
264
 
259
265
  function App() {
260
266
  return (
@@ -278,7 +284,7 @@ function App() {
278
284
  Control access based on user roles:
279
285
 
280
286
  ```tsx
281
- import { WhenRoles, WhenWorkspaceRoles } from '@buildbase/sdk';
287
+ import { WhenRoles, WhenWorkspaceRoles } from '@buildbase/sdk/react';
282
288
 
283
289
  function AdminPanel() {
284
290
  return (
@@ -312,7 +318,7 @@ import {
312
318
  WhenWorkspaceFeatureDisabled,
313
319
  WhenUserFeatureEnabled,
314
320
  WhenUserFeatureDisabled,
315
- } from '@buildbase/sdk';
321
+ } from '@buildbase/sdk/react';
316
322
 
317
323
  function FeatureExample() {
318
324
  return (
@@ -344,7 +350,7 @@ function FeatureExample() {
344
350
  Use the `useUserFeatures` hook to check feature flags programmatically:
345
351
 
346
352
  ```tsx
347
- import { useUserFeatures } from '@buildbase/sdk';
353
+ import { useUserFeatures } from '@buildbase/sdk/react';
348
354
 
349
355
  function FeatureCheck() {
350
356
  const { features, isFeatureEnabled, refreshFeatures } = useUserFeatures();
@@ -364,7 +370,7 @@ Control UI visibility based on the current workspace’s subscription. Subscript
364
370
  ### Subscription Gate Components
365
371
 
366
372
  ```tsx
367
- import { WhenSubscription, WhenNoSubscription, WhenSubscriptionToPlans } from '@buildbase/sdk';
373
+ import { WhenSubscription, WhenNoSubscription, WhenSubscriptionToPlans } from '@buildbase/sdk/react';
368
374
 
369
375
  function BillingExample() {
370
376
  return (
@@ -418,7 +424,7 @@ All gates must be used inside **SubscriptionContextProvider** (included in SaaSO
418
424
  Use the hook when you need subscription data or a manual refetch (e.g. after returning from Stripe checkout):
419
425
 
420
426
  ```tsx
421
- import { useSubscriptionContext } from '@buildbase/sdk';
427
+ import { useSubscriptionContext } from '@buildbase/sdk/react';
422
428
 
423
429
  function SubscriptionStatus() {
424
430
  const { response, loading, refetch } = useSubscriptionContext();
@@ -455,7 +461,7 @@ Control UI based on trial state. Works with Stripe-native trials (both card-requ
455
461
  ### Trial Gate Components
456
462
 
457
463
  ```tsx
458
- import { WhenTrialing, WhenNotTrialing, WhenTrialEnding } from '@buildbase/sdk';
464
+ import { WhenTrialing, WhenNotTrialing, WhenTrialEnding } from '@buildbase/sdk/react';
459
465
 
460
466
  function TrialExample() {
461
467
  return (
@@ -492,7 +498,7 @@ All trial gates support `loadingComponent` and `fallbackComponent` props.
492
498
  Hook that computes trial information from the subscription context:
493
499
 
494
500
  ```tsx
495
- import { useTrialStatus } from '@buildbase/sdk';
501
+ import { useTrialStatus } from '@buildbase/sdk/react';
496
502
 
497
503
  function TrialInfo() {
498
504
  const { isTrialing, daysRemaining, trialEndsAt, isTrialEnding } = useTrialStatus();
@@ -557,7 +563,7 @@ Everything else is built-in — permission handling, subscribe/unsubscribe, sett
557
563
  Manage custom user attributes (key-value pairs):
558
564
 
559
565
  ```tsx
560
- import { useUserAttributes } from '@buildbase/sdk';
566
+ import { useUserAttributes } from '@buildbase/sdk/react';
561
567
 
562
568
  function UserProfile() {
563
569
  const { attributes, isLoading, updateAttribute, updateAttributes, refreshAttributes } =
@@ -589,7 +595,7 @@ function UserProfile() {
589
595
  The `useSaaSWorkspaces` hook provides comprehensive workspace management:
590
596
 
591
597
  ```tsx
592
- import { useSaaSWorkspaces } from '@buildbase/sdk';
598
+ import { useSaaSWorkspaces } from '@buildbase/sdk/react';
593
599
 
594
600
  function WorkspaceManager() {
595
601
  const {
@@ -640,7 +646,7 @@ Display subscription plans and pricing on public pages (e.g. marketing site, pri
640
646
  Fetches public plans by slug. Returns `items` (features, limits, quotas) and `plans` (with pricing). You construct the layout from this data:
641
647
 
642
648
  ```tsx
643
- import { usePublicPlans } from '@buildbase/sdk';
649
+ import { usePublicPlans } from '@buildbase/sdk/react';
644
650
 
645
651
  function PublicPricingPage() {
646
652
  const { items, plans, loading, error } = usePublicPlans('main-pricing');
@@ -663,7 +669,7 @@ function PublicPricingPage() {
663
669
  Use the `PricingPage` component with a render-prop pattern:
664
670
 
665
671
  ```tsx
666
- import { PricingPage } from '@buildbase/sdk';
672
+ import { PricingPage } from '@buildbase/sdk/react';
667
673
 
668
674
  function PublicPricingPage() {
669
675
  return (
@@ -748,7 +754,7 @@ import {
748
754
  getBasePriceCents,
749
755
  getQuotaDisplayValue,
750
756
  formatQuotaWithPrice,
751
- } from '@buildbase/sdk';
757
+ } from '@buildbase/sdk/react';
752
758
 
753
759
  // Display price for a plan version in a currency
754
760
  const variant = getPricingVariant(planVersion, 'usd');
@@ -786,7 +792,7 @@ Use the SDK hooks inside your React app. Quota gate components (see [Quota Gates
786
792
  #### Record Usage
787
793
 
788
794
  ```tsx
789
- import { useRecordUsage, useSaaSWorkspaces } from '@buildbase/sdk';
795
+ import { useRecordUsage, useSaaSWorkspaces } from '@buildbase/sdk/react';
790
796
 
791
797
  function SendEmailButton() {
792
798
  const { currentWorkspace } = useSaaSWorkspaces();
@@ -816,7 +822,7 @@ function SendEmailButton() {
816
822
  #### Check Single Quota Status
817
823
 
818
824
  ```tsx
819
- import { useQuotaUsageStatus, useSaaSWorkspaces } from '@buildbase/sdk';
825
+ import { useQuotaUsageStatus, useSaaSWorkspaces } from '@buildbase/sdk/react';
820
826
 
821
827
  function QuotaStatusBar({ quotaSlug }: { quotaSlug: string }) {
822
828
  const { currentWorkspace } = useSaaSWorkspaces();
@@ -841,7 +847,7 @@ function QuotaStatusBar({ quotaSlug }: { quotaSlug: string }) {
841
847
  #### Check All Quotas
842
848
 
843
849
  ```tsx
844
- import { useAllQuotaUsage, useSaaSWorkspaces } from '@buildbase/sdk';
850
+ import { useAllQuotaUsage, useSaaSWorkspaces } from '@buildbase/sdk/react';
845
851
 
846
852
  function QuotaDashboard() {
847
853
  const { currentWorkspace } = useSaaSWorkspaces();
@@ -866,7 +872,7 @@ function QuotaDashboard() {
866
872
  #### Usage Logs
867
873
 
868
874
  ```tsx
869
- import { useUsageLogs, useSaaSWorkspaces } from '@buildbase/sdk';
875
+ import { useUsageLogs, useSaaSWorkspaces } from '@buildbase/sdk/react';
870
876
 
871
877
  function UsageLogsTable() {
872
878
  const { currentWorkspace } = useSaaSWorkspaces();
@@ -1077,7 +1083,7 @@ import {
1077
1083
  WhenQuotaExhausted,
1078
1084
  WhenQuotaOverage,
1079
1085
  WhenQuotaThreshold,
1080
- } from '@buildbase/sdk';
1086
+ } from '@buildbase/sdk/react';
1081
1087
 
1082
1088
  function Dashboard() {
1083
1089
  return (
@@ -1145,7 +1151,7 @@ All gates must be used inside `QuotaUsageContextProvider` (included in `SaaSOSPr
1145
1151
  Use the hook when you need raw quota data or a manual refetch (e.g. after a bulk operation):
1146
1152
 
1147
1153
  ```tsx
1148
- import { useQuotaUsageContext } from '@buildbase/sdk';
1154
+ import { useQuotaUsageContext } from '@buildbase/sdk/react';
1149
1155
 
1150
1156
  function QuotaDebug() {
1151
1157
  const { quotas, loading, refetch } = useQuotaUsageContext();
@@ -1180,7 +1186,7 @@ function QuotaDebug() {
1180
1186
  Use the pre-built `BetaForm` component for signup/waitlist forms:
1181
1187
 
1182
1188
  ```tsx
1183
- import { BetaForm } from '@buildbase/sdk';
1189
+ import { BetaForm } from '@buildbase/sdk/react';
1184
1190
 
1185
1191
  function SignupPage() {
1186
1192
  return (
@@ -1201,7 +1207,8 @@ function SignupPage() {
1201
1207
  Subscribe to SDK events for user and workspace changes:
1202
1208
 
1203
1209
  ```tsx
1204
- import { SaaSOSProvider, eventEmitter } from '@buildbase/sdk';
1210
+ import { SaaSOSProvider } from '@buildbase/sdk/react';
1211
+ import { eventEmitter } from '@buildbase/sdk';
1205
1212
 
1206
1213
  // In your provider configuration
1207
1214
  <SaaSOSProvider
@@ -1249,7 +1256,7 @@ The SDK handles errors internally: API failures, auth errors, and component erro
1249
1256
  Access OS-level settings:
1250
1257
 
1251
1258
  ```tsx
1252
- import { useSaaSSettings } from '@buildbase/sdk';
1259
+ import { useSaaSSettings } from '@buildbase/sdk/react';
1253
1260
 
1254
1261
  function SettingsExample() {
1255
1262
  const { settings, getSettings } = useSaaSSettings();
@@ -1296,7 +1303,8 @@ All SDK API clients extend a shared base class and are exported from the package
1296
1303
  Get OS config from `useSaaSOs()` and instantiate API classes when you need low-level access; otherwise prefer the high-level hooks (`useSaaSWorkspaces`, `useUserAttributes`, `useSaaSSettings`, etc.):
1297
1304
 
1298
1305
  ```tsx
1299
- import { UserApi, WorkspaceApi, SettingsApi, useSaaSOs } from '@buildbase/sdk';
1306
+ import { useSaaSOs } from '@buildbase/sdk/react';
1307
+ import { UserApi, WorkspaceApi, SettingsApi } from '@buildbase/sdk';
1300
1308
 
1301
1309
  const os = useSaaSOs();
1302
1310
  const workspaceApi = new WorkspaceApi({
@@ -1361,10 +1369,16 @@ All TypeScript types are exported for type safety. See the [TypeScript definitio
1361
1369
  interface IAuthConfig {
1362
1370
  clientId: string; // OAuth client ID
1363
1371
  redirectUrl: string; // OAuth redirect URL
1364
- callbacks?: {
1372
+ callbacks: {
1373
+ // Required: restore session on page refresh (reads httpOnly cookie via server endpoint)
1374
+ getSession: () => Promise<string | null>;
1375
+ // Required: exchange OAuth code for sessionId (sets httpOnly cookie on server)
1365
1376
  handleAuthentication: (code: string) => Promise<{ sessionId: string }>;
1366
- onSignOut?: () => Promise<void>;
1377
+ // Required: clear session on sign out (clears httpOnly cookie on server)
1378
+ onSignOut: () => Promise<void>;
1379
+ // Optional: listen to SDK events
1367
1380
  handleEvent?: (eventType: EventType, data: EventData) => void | Promise<void>;
1381
+ // Optional: called before workspace switch
1368
1382
  onWorkspaceChange?: (params: OnWorkspaceChangeParams) => Promise<void>;
1369
1383
  };
1370
1384
  }
@@ -1372,10 +1386,15 @@ interface IAuthConfig {
1372
1386
  interface OnWorkspaceChangeParams {
1373
1387
  workspace: IWorkspace;
1374
1388
  user: AuthUser | null;
1375
- role: string | null; // User's role in this workspace
1389
+ role: string | null;
1376
1390
  }
1377
1391
  ```
1378
1392
 
1393
+ **Session flow** (same pattern as next-auth):
1394
+ - Session token is stored in an **httpOnly cookie** (set by your server, not readable by JS)
1395
+ - On page refresh, the SDK calls `getSession()` once to restore the session
1396
+ - Session data (user info) lives **in-memory only** (React context) — no localStorage
1397
+
1379
1398
  ### Validation Requirements
1380
1399
 
1381
1400
  - **serverUrl**: Must be a valid URL (e.g., `https://api.example.com`)
@@ -1403,7 +1422,7 @@ interface OnWorkspaceChangeParams {
1403
1422
  ### Pattern 1: Protected Routes
1404
1423
 
1405
1424
  ```tsx
1406
- import { WhenAuthenticated, WhenUnauthenticated } from '@buildbase/sdk';
1425
+ import { WhenAuthenticated, WhenUnauthenticated } from '@buildbase/sdk/react';
1407
1426
 
1408
1427
  function App() {
1409
1428
  return (
@@ -1430,7 +1449,7 @@ function ProtectedRoutes() {
1430
1449
  ### Pattern 2: Role-Based Navigation
1431
1450
 
1432
1451
  ```tsx
1433
- import { WhenRoles } from '@buildbase/sdk';
1452
+ import { WhenRoles } from '@buildbase/sdk/react';
1434
1453
 
1435
1454
  function Navigation() {
1436
1455
  return (
@@ -1453,7 +1472,7 @@ function Navigation() {
1453
1472
  ### Pattern 3: Workspace Context Provider
1454
1473
 
1455
1474
  ```tsx
1456
- import { useSaaSWorkspaces } from '@buildbase/sdk';
1475
+ import { useSaaSWorkspaces } from '@buildbase/sdk/react';
1457
1476
  import { createContext, useContext } from 'react';
1458
1477
 
1459
1478
  const WorkspaceContext = createContext(null);
@@ -1472,7 +1491,7 @@ export function useWorkspace() {
1472
1491
  ### Pattern 4: Feature Gated Components
1473
1492
 
1474
1493
  ```tsx
1475
- import { WhenWorkspaceFeatureEnabled } from '@buildbase/sdk';
1494
+ import { WhenWorkspaceFeatureEnabled } from '@buildbase/sdk/react';
1476
1495
 
1477
1496
  function Dashboard() {
1478
1497
  return (
@@ -1494,7 +1513,7 @@ function Dashboard() {
1494
1513
  ### Pattern 4b: Subscription-Gated UI
1495
1514
 
1496
1515
  ```tsx
1497
- import { WhenSubscription, WhenNoSubscription, WhenSubscriptionToPlans } from '@buildbase/sdk';
1516
+ import { WhenSubscription, WhenNoSubscription, WhenSubscriptionToPlans } from '@buildbase/sdk/react';
1498
1517
 
1499
1518
  function BillingPage() {
1500
1519
  return (
@@ -1520,7 +1539,7 @@ SubscriptionContextProvider is included in SaaSOSProvider by default, so no extr
1520
1539
  ### Pattern 4c: Quota-Gated UI
1521
1540
 
1522
1541
  ```tsx
1523
- import { WhenQuotaAvailable, WhenQuotaExhausted, WhenQuotaThreshold } from '@buildbase/sdk';
1542
+ import { WhenQuotaAvailable, WhenQuotaExhausted, WhenQuotaThreshold } from '@buildbase/sdk/react';
1524
1543
 
1525
1544
  function FeatureWithQuota() {
1526
1545
  return (
@@ -1546,7 +1565,7 @@ QuotaUsageContextProvider is included in SaaSOSProvider by default, so no extra
1546
1565
  ### Pattern 5: Handling Workspace Changes
1547
1566
 
1548
1567
  ```tsx
1549
- import { useSaaSWorkspaces } from '@buildbase/sdk';
1568
+ import { useSaaSWorkspaces } from '@buildbase/sdk/react';
1550
1569
  import { useEffect } from 'react';
1551
1570
 
1552
1571
  function App() {
@@ -1692,7 +1711,7 @@ useEffect(() => {
1692
1711
  **Solution**: Ensure CSS is imported:
1693
1712
 
1694
1713
  ```tsx
1695
- import '@buildbase/sdk/dist/saas-os.css';
1714
+ import '@buildbase/sdk/css';
1696
1715
  ```
1697
1716
 
1698
1717
  ### FAQ
@@ -1710,7 +1729,7 @@ A: Use the `trigger` render prop to fully customize the UI.
1710
1729
  A: No, the SDK manages one current workspace at a time. Use `switchToWorkspace()` (runs `onWorkspaceChange` first) or `setCurrentWorkspace()` (direct set, bypasses callback).
1711
1730
 
1712
1731
  **Q: How do I handle offline scenarios?**
1713
- A: The SDK stores session data in localStorage. Handle offline scenarios in your `handleAuthentication` callback.
1732
+ A: The session token is stored in an httpOnly cookie (set by your server). On page refresh, the SDK calls your `getSession` callback to restore it. Handle offline scenarios in your `handleAuthentication` and `getSession` callbacks.
1714
1733
 
1715
1734
  **Q: Can I use this without TypeScript?**
1716
1735
  A: Yes, but TypeScript is recommended for better developer experience.
@@ -1881,6 +1900,133 @@ refreshWorkspaces();
1881
1900
  await fetchWorkspaces();
1882
1901
  ```
1883
1902
 
1903
+ ## Server-Side Usage
1904
+
1905
+ The SDK also works on the server — API routes, background jobs, webhooks, cron tasks. Zero React dependency.
1906
+
1907
+ ```
1908
+ @buildbase/sdk Server: BuildBase() factory, API classes, types, utilities
1909
+ @buildbase/sdk/react Client: React hooks, providers, gate components (documented above)
1910
+ ```
1911
+
1912
+ ### Setup (Next.js)
1913
+
1914
+ Configure once, use everywhere. Same pattern as Auth.js.
1915
+
1916
+ ```ts
1917
+ // lib/buildbase.ts
1918
+ import BuildBase from '@buildbase/sdk'
1919
+ import { cookies } from 'next/headers'
1920
+
1921
+ export const { auth, workspace, subscription, usage, plans, invoices, users, features, settings, withSession, client } = BuildBase({
1922
+ serverUrl: process.env.BUILDBASE_URL!,
1923
+ orgId: process.env.BUILDBASE_ORG_ID!,
1924
+ getSessionId: async () => {
1925
+ const c = await cookies()
1926
+ return c.get('bb-session-id')?.value ?? null
1927
+ },
1928
+ })
1929
+ ```
1930
+
1931
+ Use in API routes — session is resolved automatically:
1932
+
1933
+ ```ts
1934
+ // app/api/workspace/route.ts
1935
+ import { auth, workspace, subscription } from '@/lib/buildbase'
1936
+
1937
+ export async function GET() {
1938
+ const session = await auth()
1939
+ if (!session) return Response.json({ error: 'Unauthorized' }, { status: 401 })
1940
+
1941
+ const workspaces = await workspace.list()
1942
+ return Response.json({ workspaces })
1943
+ }
1944
+ ```
1945
+
1946
+ ### Setup (Express)
1947
+
1948
+ No `getSessionId` callback — use `withSession()` per-request instead.
1949
+
1950
+ ```ts
1951
+ // buildbase.ts
1952
+ import BuildBase from '@buildbase/sdk'
1953
+
1954
+ const bb = BuildBase({
1955
+ serverUrl: process.env.BUILDBASE_URL!,
1956
+ orgId: process.env.BUILDBASE_ORG_ID!,
1957
+ })
1958
+
1959
+ export const { withSession, plans } = bb
1960
+ ```
1961
+
1962
+ ```ts
1963
+ // routes
1964
+ app.get('/workspaces', async (req, res) => {
1965
+ const { workspace } = withSession(req.headers['x-session-id'])
1966
+ res.json(await workspace.list())
1967
+ })
1968
+
1969
+ app.post('/usage', async (req, res) => {
1970
+ const { usage } = withSession(req.headers['x-session-id'])
1971
+ const result = await usage.record(req.body.workspaceId, {
1972
+ quotaSlug: 'api-calls',
1973
+ quantity: 1,
1974
+ })
1975
+ res.json(result)
1976
+ })
1977
+ ```
1978
+
1979
+ ### Background Jobs / Webhooks
1980
+
1981
+ For service accounts (no user session), use `withSession()` with a service token:
1982
+
1983
+ ```ts
1984
+ import { withSession } from '@/lib/buildbase'
1985
+
1986
+ const bb = withSession(process.env.SERVICE_SESSION_ID!)
1987
+
1988
+ // Record usage from a webhook
1989
+ await bb.usage.record(workspaceId, {
1990
+ quotaSlug: 'uploads',
1991
+ quantity: 1,
1992
+ source: 'webhook:file.processed',
1993
+ })
1994
+
1995
+ // Check subscription
1996
+ const sub = await bb.subscription.get(workspaceId)
1997
+ ```
1998
+
1999
+ ### Server-Side Actions Reference
2000
+
2001
+ | Module | Methods |
2002
+ |--------|---------|
2003
+ | `workspace` | `list`, `get`, `create`, `update`, `delete` |
2004
+ | `users` | `list`, `invite`, `remove`, `updateRole`, `getProfile`, `updateProfile` |
2005
+ | `subscription` | `get`, `checkout`, `update`, `cancel`, `resume`, `getBillingPortalUrl` |
2006
+ | `plans` | `getGroup`, `getVersions`, `getPublic`, `getVersion` |
2007
+ | `invoices` | `list`, `get` |
2008
+ | `usage` | `record`, `getQuota`, `getAll`, `getLogs` |
2009
+ | `settings` | `get` |
2010
+ | `features` | `list`, `update` |
2011
+
2012
+ ### BuildBase Config Options
2013
+
2014
+ ```ts
2015
+ BuildBase({
2016
+ serverUrl: '...', // Required
2017
+ orgId: '...', // Required
2018
+ getSessionId: async () => ..., // Session resolver (Next.js: read cookie)
2019
+ timeout: 30_000, // Request timeout in ms (default: 30s)
2020
+ maxRetries: 2, // Retry on network errors / 5xx
2021
+ debug: true, // Log all API calls to console
2022
+ headers: { 'X-Source': 'cron' }, // Custom headers on every request
2023
+ onError: (err, ctx) => { // Centralized error callback
2024
+ Sentry.captureException(err, { extra: ctx })
2025
+ },
2026
+ fetch: customFetch, // Replace global fetch (testing, proxying)
2027
+ })
2028
+ ```
2029
+
1884
2030
  ## 🤝 Contributing
1885
2031
 
1886
2032
  1. Fork the repository