avada-server-bridge
v1.0.1
Published
Server-side logic toolkit for Avada Shopify apps — BigQuery hub modules, TTL caching, Koa handlers and core primitives for Firebase Functions.
Readme
avada-server-bridge
Shared server-side logic toolkit for Avada Shopify apps (Cookie Bar, Accessibility, Age Verification, Withdrawal Forms, …).
Vision & naming: shared code splits by runtime environment. Anything that runs in the browser — components, styles, i18n bundles, hooks — lives in
avada-ui-bridge. Anything that runs on the server (Firebase Functions / Koa) lives here. One lib per side of the wire:packages/assetsinstalls ui-bridge,packages/functionsinstalls server-bridge.
| Package | Runs in | Holds | Example |
|---|---|---|---|
| avada-ui-bridge | Browser | Components, styles, i18n, hooks (incl. headless useLawNews/useLawNewsToggle) | <LawNews items={...} /> |
| avada-server-bridge | Node | Server modules + core primitives (BigQuery hub, caching, Koa handlers) | createLawNewsModule({app}) |
Install
yarn workspace @avada/functions add avada-server-bridge # pin EXACT version, same convention as avada-ui-bridgeStandard root entry, dual CJS/ESM with typed exports (validated by publint + arethetypeswrong):
import {createLawNewsModule} from 'avada-server-bridge';Keep
@google-cloud/bigqueryas a direct dependency ofpackages/functions. The kit declares it as a required peer — yarn will warn (not fail) if a prune tool removes it; without it law-news fails soft to hidden at runtime.
Modules
law-news
Per-app feed of legal/privacy news from the shared BigQuery hub (avada-crm.app_seaAccessibility.law_news[_config], rows split by app). Reads fail soft (content → [], showCard → false) so a hub outage never breaks Home. TTL-cached (content 10 min, showCard 60 s). setShowCard uses DML MERGE (not streaming insert — stream buffer would swallow the next toggle).
// packages/functions/src/config/lawNews.js — the ONLY per-app law-news code left
import {createLawNewsModule} from 'avada-server-bridge';
export const lawNews = createLawNewsModule({app: 'withdrawal-forms'});
// routes/api.js
import {lawNews} from '@functions/config/lawNews';
lawNews.registerRoutes(router); // GET /law-news + PUT /law-news/configEvery default is overridable — projectId, dataset, contentTable, configTable, keyEnv (default LAW_NEWS_BQ_KEY, base64 SA key), saKeyPath (default serviceAccount.lawnews.json, gitignored local dev key resolved from the functions cwd), limit, contentTtlMs, showCardTtlMs, logger, or inject a prebuilt client (tests). Set contentTtlMs: 0 / showCardTtlMs: 0 to disable caching entirely — every request queries BigQuery directly; fine for tiny datasets that must reflect BA edits instantly. Need only the pieces? createLawNewsRepository / createLawNewsKoaHandlers / registerLawNewsRoutes are exported individually.
Frontend side (in avada-ui-bridge ≥1.8.0): useLawNews({useFetchApi}) feeds <LawNews /> on Home; useLawNewsToggle({useFetchApi, useEditApi}) powers the DevZone master switch (app renders its own AdvancedToggle). ~10 glue lines per app.
Core primitives (build future modules on these)
createHubBigQueryClient({projectId, keyEnv, saKeyPath, credentials})— BQ client for a hub project outside the app's own GCP project; credential chain: explicit → base64 env → local SA file → ADC.createTtlCache(ttlMs)— read-through cache for warm serverless instances; caches falsy values correctly.parseBase64SaKey(raw)— decode single-line .env SA keys.KoaLikeContext/Router— structural types matching the @avada/core template; no koa dependency.
Adding a new module
src/modules/<name>/— kebab-case files, one concern per file, <200 lines.- Design rule: config over fork — hardcode nothing an app might vary; ship defaults so
create<Name>Module({app})works out of the box. Inject app dependencies (BQ client, logger) as options. - Reads that feed merchant UI fail soft; writes return
{success, error?}instead of throwing. - Re-export from
src/index.ts, add tests intests/, update this README. - If the feature has a browser half (component/hook) → that half goes to
avada-ui-bridge, wired to this module's routes. npm run typecheck && npm test && npm run build && npm run check:packagemust pass (prepublishOnlyenforces this automatically).
Develop & publish
npm run build # tsup → dist/index.{js,mjs,d.ts}
npm run typecheck
npm test # vitest (fake BQ client — no live hub needed)
npm run check:package # publint + arethetypeswrong — package metadata/types must be greenRelease: bump version + CHANGELOG entry → manual npm publish (account vuavada, 2FA). prepublishOnly runs the full gate (clean → typecheck → test → build → check:package) — a broken package cannot ship. Apps pin the exact version in packages/functions.
License: UNLICENSED — proprietary, for Avada applications only. The package is on the public npm registry (readable by anyone) but grants no usage rights to third parties.
