@farthershore/farthershore-js
The browser client, React hooks, and managed component kit.
The browser client, React hooks, and managed component kit.
This guide targets @farthershore/farthershore-js 0.32.0. See the generated
client exports, React exports,
and component exports for exact signatures.
@farthershore/farthershore-js is the Frontend SDK — the browser integration
layer between a static frontend artifact and the platform. Your frontend code
never sees platform URLs or auth endpoints; it expresses intent and the SDK
decides where each request goes (platform-management calls vs your product's own
features), how it's authenticated, and the host/env scoping.
It versions independently from @farthershore/business
and @farthershore/backend.
pnpm add @farthershore/farthershore-js # react is an optional peer (for /react)
import { createFartherShoreClient } from "@farthershore/farthershore-js";
const fs = createFartherShoreClient(); // zero config on platform-served portals
const { business, plans } = await fs.bootstrap(); // → platform (discover this host)
const session = await fs.auth.getSession(); // → safe identity/session metadata
const usage = await fs.usage.snapshot(); // → platform
const forecast = await fs.route.get("/forecast?city=NYC"); // → your product API
In a test-persona environment, start a browser session with
farthershore persona login; the CLI opens the platform-owned
/persona-sign-in bridge. The one-time secret remains fragment-only until its
same-origin exchange sets a server-owned HttpOnly cookie. Custom or template
JavaScript reads session.authSession and never receives or stores a bearer or
access key. Call fs.auth.signOut() to revoke that server session.
createFartherShoreClient(config?) never throws at construction — it
needs no config at all: the platform injects window.__FS_CONFIG__ into
served portals, and farthershore frontend dev|preview injects the same shim
during local development, so the SDK reads its connection lazily in every
environment. Platform-infrastructure values (coreUrl, portalHost,
businessId, gatewayUrl, environmentId) are injected by the edge, never
passed here.
Builder config fields (all optional): getToken, organizationId, apiKey,
mock, retry, fetch, onError, onLimitExceeded, onUnauthorized.
apiKey and fs.setApiKey() remain available for deliberate Gateway API-key
clients, such as a headless integration exercising product routes. They are not
persona browser sign-in APIs: hosted portal code must use the server-owned
session and must not place an API key in browser storage.
The client routes each namespace to the platform's management API or your product's own API:
| Namespace | Routes to | Methods |
|---|---|---|
fs.bootstrap() | platform (public resolve) | discover business and environment metadata (memoized) |
fs.business | bootstrap | get(), resources() |
fs.auth | platform | getSession(), signOut(), setToken(), gatewayContextToken() |
fs.keys | platform | list(), create(), revoke(), rotate() |
fs.usage | platform | summary(), events(), snapshot() |
fs.billing | platform | Subscription, billing portal, cancellation, plan-change, and getBillPreview() |
fs.plans | platform / bootstrap | list(), getPlanOffers(), subscribe(), startOnboarding() |
fs.entitlements | platform / bootstrap | Read the subscriber's enforced limits and current usage |
fs.resources(type) | platform | list(), get(), create(), update(), delete(), count() |
fs.organizations, fs.team, fs.rbac | platform | Organization contexts, membership, role assignment, and RBAC catalog/settings/roles |
fs.notifications, fs.auditLogs | platform | Notification preferences and paginated audit history |
fs.route.get(path) / fs.route.post(path, body) | your product API | fetch(path), json(path) |
Platform-management and product-API calls use the SDK-managed browser context.
In a managed portal, let the SDK obtain the gateway context; do not copy browser
credentials to your backend or construct unsigned identity headers. Use
apiKey/setApiKey() only when intentionally building an API-key-authenticated
Gateway client, never as a substitute for customer or persona browser sign-in.
/react)Hooks live in a subpath; react is an optional peer. Each hook returns
{ data, error, isLoading, isError, isSuccess, refetch, queryKey } plus its
mutations.
import { createFartherShoreClient } from "@farthershore/farthershore-js";
import {
FartherShoreProvider,
useApiKeys,
} from "@farthershore/farthershore-js/react";
const fs = createFartherShoreClient(); // config arrives via the injected channel
function App() {
return (
<FartherShoreProvider client={fs}>
<Portal />
</FartherShoreProvider>
);
}
function ApiKeys() {
const { data, isLoading, create, revoke, rotate } = useApiKeys();
// …
}
| Hook | Returns |
|---|---|
useFartherShore() | The raw client. |
useBootstrap(), useBusiness(), useDeclaredResources() | Bootstrap + product + declared resources. |
useSession() | Session and safe authSession identity metadata (+ signOut). |
useApiKeys() | Keys (+ create / revoke / rotate). |
useUsage(), usePinnedUsageRows() | Usage snapshot + pinned billing rows. |
useBilling() | Subscription (+ openBillingPortal). |
usePlans() | The plan catalog (kind, recurring price, trial — never per-unit rates). |
useBillPreview() | The subscriber's bill preview; switch on disclosure. |
useEntitlements(), useResourceLimitUsage() | Subscriber-state and enforced-limit usage. |
useRouteRateLimit() | Rate-limit snapshot for a product API route. |
useMe(), useSubscriptionContexts() | The current subscriber + their subscription contexts. |
useTeam(), useAuditLogs(), usePaginatedAuditLogs() | Team + audit logs. |
useResourcesList(), useResource(), useResourceUsage() | Counted resources. |
useUpgrade(), useResourceCap(), useReconcileAfterCheckout() | Upgrade targets; checkout-return reconciliation. |
useOrganization() | Multi-org reactivity (the provider mounts the layer automatically). |
useUsage() reads the usage snapshot, a bounded event sample marked
exact: false. It is useful for recent activity and troubleshooting, not an
invoice total or proof of settlement. Use useBillPreview() for platform-rated
money and honor its disclosure mode. Loading, empty, and failed are separate UI
states: a failed query must not display a zero balance. See
bill preview.
When a product has rbac enabled, useFsAuth() also
returns the signed-in user's resolved access:
const { roles, permissions, hasPermission } = useFsAuth();
if (hasPermission("reports:write")) {
// show the edit control
}
roles / permissions are server-resolved (read from /me, never
decoded from the token client-side); permissions of ["*"] grants
everything (owner / RBAC-disabled / personal org).<RequireAuth requirePermission="reports:write">…</RequireAuth> renders its
fallback when the user lacks the permission.fs.rbac.settings / fs.rbac.catalog() / fs.rbac.roles and
useTeam().assignRoles drive the customer's role-management UI.Client-side checks are UX only — hide controls, never enforce. The edge
permission constraint is the security boundary.
/components)The managed component kit. Subscriber data components such as PlansTable,
UsageCard, BillingSummary, and ApiKeysPanel work with zero data props under
<FartherShoreRoot> through SDK hooks. Rendering primitives can require input:
BusinessApiReference requires an OpenAPI document, as shown below. Consult
each component's props rather than assuming it fetches its own data. The
components accept className, are headless, and do not ship a CSS
entrypoint. Style their
rendered markup with your application's CSS.
import {
FartherShoreRoot,
PlansTable,
UsageCard,
BillingSummary,
ApiKeysPanel,
} from "@farthershore/farthershore-js/components";
<FartherShoreRoot>
<PlansTable />
<UsageCard />
<BillingSummary />
<ApiKeysPanel />
</FartherShoreRoot>;
| Group | Components |
|---|---|
| Mount + chrome | FartherShoreRoot, useBoot (from /react), FsErrorBoundary, FsFooter, FsSplash |
| Auth (managed) | FsAuthProvider, useFsAuth + useOptionalFsAuth (from /react), FsSignIn, FsSignInButton, FsSignOutButton, FsUserButton, SignedIn, SignedOut, AuthLoading |
| Auth-gated routing | RequireAuth, useAuthGuard, planAuthGuard, resolveSignInDestination |
| Data | PlansTable, ApiKeysPanel, UsageCard, BillingSummary, RoutePanel, ResourcesPanel, DocsLegal |
| Gating + prompts | PermissionGate, UpgradePrompt, OrgSwitcher, FsLimitBoundary, useLimitHandler |
| Data-rich | TeamPanel, CancelSubscription, TrialBanner, ResourceLimitUsageCard, OnboardingView, AutoKeyBanner, AuditLogTable, RateLimitDisplay, BillPreviewCard |
| Composite | FartherShoreApp, FsPricing — one-tag portal |
| Docs | BusinessDocs, BusinessApiReference, useBusinessDocs, plus the composable shell parts, markdown renderers, and OpenAPI helpers |
<FartherShoreRoot> combines the provider, bootstrap gate, theme and branding
chrome, and managed auth strategy in one wrapper.
BusinessApiReference is the docs-styled API reference renderer. Feed it an OpenAPI 3.x document, or a compatible subset with info, servers, tags, paths, request bodies, responses, and components.schemas. It is intentionally separate from BusinessDocs: prose docs and endpoint reference can live side by side, like a managed gateway docs portal.
import {
BusinessApiReference,
type BusinessOpenApiDocument,
} from "@farthershore/farthershore-js/components";
const spec = {
openapi: "3.1.0",
info: { title: "Weather API", version: "2026-06-28" },
servers: [{ url: "https://api.weather.example" }],
paths: {
"/v1/forecast": {
get: {
summary: "Get forecast",
tags: ["Forecasts"],
responses: { "200": { description: "Forecast returned." } },
},
},
},
} satisfies BusinessOpenApiDocument;
<BusinessApiReference document={spec} />;
Branch on FartherShoreApiError.code against the platform deny vocabulary. See
response & deny codes.
import {
FartherShoreApiError,
LimitExceededError,
FartherShoreRateLimitedError,
FS_DENY_CODES,
} from "@farthershore/farthershore-js";
import { isThrottled, isRetryable } from "@farthershore/farthershore-js/errors";
try {
await fs.route.get("/v1/cron-jobs");
} catch (err) {
if (
err instanceof FartherShoreApiError &&
err.code === FS_DENY_CODES.rate_limited
) {
// back off and retry
}
}
The root exports five common classes: FartherShoreError,
FartherShoreApiError, LimitExceededError,
FartherShoreRateLimitedError, and FartherShoreConfigError. The /errors
subpath also exports the network, abort, not-ready, request-too-large, spend,
concurrency, provider, adaptive-throttle, feature, permission, lifecycle,
subscription-conflict, changed-offer, and billing-not-configured subclasses.
It also provides isRetryable, isThrottled, isBillingNotConfigured, and the
deny-envelope parsers. Import specialized errors and guards from /errors.
| Task | Guide | Full API |
|---|---|---|
| Mount a hosted portal | Frontend overview, components | Components |
| Build custom subscriber screens | Auth, access-aware UI | React hooks |
| Add org-configurable controls | Custom components, permission gates | Components |
| Call your API and handle denials | Consume an API | Errors |
| Call a provider without exposing a secret | Integrations, variables | Client |
| Compose a documentation shell | Use BusinessDocs or BusinessApiReference, then customize layout | Docs chrome |
| Test rendering without live credentials | Use the test workflow below | Test utilities |
/test-utils exports createMockFartherShoreClient,
FartherShoreTestProvider, and typed error builders such as mockApiError and
mockLimitExceeded. Override the particular client method your component uses,
then render it under the test provider. Assert loading, success, empty, and
failure states separately; include the denied mutation path even if the button
usually hides.
Mock mode proves presentation, not gateway enforcement, pricing, or hosted configuration. Repeat critical flows in a preview with real persona sessions: sign in, switch organizations, call a denied route, and sign out. Confirm stale data from the previous organization disappears and that usage failures do not render a zero bill. Never keep test API keys in shipped source.
Pure display helpers so any frontend renders the catalog identically:
isFreePlan, formatPlanPrice, formatDate, entitlementBullets,
describePlanLimit, subscriptionStatusChip, trialDaysRemaining,
paymentHealth, plus the CSV exporters (usageToCsv, downloadCsv) and
subscription helpers. Plans expose their declared kind; there is no
client-side classification and no client-side bill math — money comes only
from the bill preview API.
Pin this SDK exactly or with a patch-only range while it is pre-1.0. It
versions independently from @farthershore/business and
@farthershore/backend; SemVer 0.x minor bumps may break, so pin the current
release exactly or use only a patch-level range until 1.0.0.