@axcode/axcloud-sdk

中文文档
1.0.0发布于 2026/11/02SEE LICENSE IN LICENSE.mdTypeScriptESM + CJS近 30 天下载 1837

SDK for building websites with AxCloud CMS and member management

原始使用文档
文档目录30 节
  1. Installation
  2. Version Tags
  3. Quick Start
  4. Anonymous public sites (no Auth0)
  5. Barnsport product client and hooks
  6. 1. Wrap your app with AxCloudProvider
  7. Customer-owned identity providers
  8. 2. Fetch content
  9. 3. Add authentication
  10. 3b. Add privacy-safe analytics
  11. 4. Use sections
  12. 4b. Section-scoped content
  13. 5. Fetch organization branding and media
  14. 6. Fetch public documents
  15. 7. Gate features by module
  16. 8. Membership applications with a section choice
  17. API Reference
  18. Provider
  19. Hooks
  20. Content Query Options
  21. Components
  22. Advanced Usage
  23. Custom API calls
  24. Access the underlying Auth0 client
  25. Security Model
  26. API Key vs Authentication Token
  27. What the API Key Protects
  28. Endpoint Security
  29. Error Handling
  30. License

@axcode/axcloud-sdk

SDK for building websites with AxCloud CMS and member management. AxCloud is headless: it owns content, publication, access control and delivery APIs, while the consuming application owns markup, layout and design.

Installation

npm install @axcode/axcloud-sdk

Version Tags

The SDK is published with two tags for different environments:

TagPurposeStabilityInstall
latestProductionStable, testednpm install @axcode/axcloud-sdk
nextDevelopment/StagingPreview, may have breaking changesnpm install @axcode/axcloud-sdk@next

Recommended production setup:

Resolve latest deliberately, then commit the exact version and lockfile:

npm install --save-exact @axcode/axcloud-sdk@latest
{
  "dependencies": {
    "@axcode/axcloud-sdk": "0.3.1"
  }
}

Do not install a moving tag in CI or during deployment. Test a specific next version in a branch when needed, then promote and pin the stable release.

Quick Start

Anonymous public sites (no Auth0)

Public product sites that do not have member login should import the dedicated provider. It mounts the SDK client, React Query cache and privacy-safe analytics without importing or initializing Auth0:

import { AxCloudPublicProvider } from '@axcode/axcloud-sdk/public';

<AxCloudPublicProvider
  orgId="barnsport"
  apiUrl="https://api.axcloud.app"
  stage="prod"
  apiKey="pk_live_barnsport_read_submit"
>
  <App />
</AxCloudPublicProvider>;

Public websites can read published CMS content through the same Auth0-free entrypoint. Draft and preview content remain available only through the authenticated Console:

import { usePageContent } from '@axcode/axcloud-sdk/public';

function HomeHero() {
  const content = usePageContent('home', { lang: 'sv', publicOnly: true });
  return <h1>{content.text('hero.title', 'Fallback title')}</h1>;
}

For non-React adapters and incremental migrations, create the same scoped Barnsport client without importing the authenticated SDK root:

import { createPublicBarnsportClient } from '@axcode/axcloud-sdk/barnsport';

const barnsport = createPublicBarnsportClient({ apiUrl, apiKey, orgId: 'barnsport' });
const providers = await barnsport.listProviders({ q: 'fotboll', limit: 5 });

Use a browser-safe key restricted to the public scopes needed by the site. Do not use AxCloudPublicProvider for member or administrator features. Existing authenticated applications continue to use AxCloudProvider unchanged.

Barnsport product client and hooks

Barnsport types, the core client and React hooks are available from a separate entrypoint so other products do not load Barnsport code:

import {
  useBarnsportProviderSearch,
  useBarnsportProvider,
  useSubmitBarnsportReview,
  useTrackBarnsportEvent,
} from '@axcode/axcloud-sdk/barnsport';

const providers = useBarnsportProviderSearch({ q: searchText, limit: 5 });
const provider = useBarnsportProvider(providerSlug);

Provider reviews and product events remain Barnsport domain data. The SDK routes them through the AxCloud Barnsport proxy; it does not store them in the generic Content service.

1. Wrap your app with AxCloudProvider

import { AxCloudProvider } from '@axcode/axcloud-sdk';

function App() {
  return (
    <AxCloudProvider
      orgId="org_ba81807b" // Your organization ID
      apiUrl="https://api.axcloud.app"
      apiKey="pk_live_..." // Public API key from Settings → API Keys
      auth0={{
        domain: 'auth.axcloud.app',
        clientId: 'your-client-id',
        redirectUri: window.location.origin,
        organization: 'org_auth0_...', // Optional Auth0 Organization ID for invite-only member sites
      }}
    >
      <Router>
        <Routes />
      </Router>
    </AxCloudProvider>
  );
}

orgId is the AxCloud organization ID used by CMS APIs, for example org_ba81807b. auth0.organization is the Auth0 Organization ID used during login, for example org_o960yILn77d4vY6u. Set auth0.organization for invite-only customer member sites so Universal Login is scoped to that customer organization.

Customer-owned identity providers

An organization may authenticate through its own Auth0 tenant while AxCloud continues to provide content, membership and authorization APIs. Configure the complete, reviewed provider tuple statically in the first-party site:

<AxCloudProvider
  orgId="org_customer"
  apiUrl="https://dev-api.axcloud.app"
  stage="dev"
  auth0={{
    identityProviderId: 'idp_customer_dev',
    domain: 'dev-auth.customer.example',
    clientId: 'customer-member-spa-client-id',
    audience: 'https://api.axcloud.app',
    organization: 'org_customer_auth0',
    redirectUri: window.location.origin,
  }}
>
  <App />
</AxCloudProvider>

identityProviderId is a public AxCloud identifier, not a credential. It is included in the SDK cache-owner signature so a provider switch or rotation cannot reuse identity-bound query data. The SDK does not discover providers or hold Auth0 Management API credentials. Keep domain, client ID, audience, organization ID, AxCloud stage and API URL together as one environment-specific configuration; never mix a customer DEV tenant with AxCloud PROD APIs.

2. Fetch content

import { useNews } from '@axcode/axcloud-sdk';

function NewsPage() {
  const { data: news, isLoading, isError } = useNews();

  if (isLoading) return <Spinner />;
  if (isError) return <Error />;

  return (
    <ul>
      {news.map((item) => (
        <li key={item.id}>{item.title}</li>
      ))}
    </ul>
  );
}

Content delivery: These hooks return only published snapshots. When the user is authenticated, access to internal, member and section content is derived from the verified token. Anonymous users receive only public content. Client-supplied status or section identifiers never grant broader access.

Pages and news can be stored once in AxCloud with translations and requested per language:

import { useContentItem, useNews } from '@axcode/axcloud-sdk';

function AboutPage({ locale }: { locale: 'sv' | 'en' }) {
  const { data: page } = useContentItem('pages', 'about-us', {
    lang: locale,
    publicOnly: true,
  });

  return <h1>{page?.title}</h1>;
}

function NewsList({ locale }: { locale: 'sv' | 'en' }) {
  const { data: news } = useNews({ lang: locale });

  return news.map((item) => <article key={item.id}>{item.title}</article>);
}

3. Add authentication

import { useAuth, LoginButton, LogoutButton, ProtectedRoute } from '@axcode/axcloud-sdk';

function Header() {
  const { isAuthenticated, user } = useAuth();

  return (
    <header>
      {isAuthenticated ? (
        <>
          <span>Welcome, {user.name}</span>
          <LogoutButton />
        </>
      ) : (
        <LoginButton>Sign In</LoginButton>
      )}
    </header>
  );
}

function MemberArea() {
  return (
    <ProtectedRoute>
      <MemberContent />
    </ProtectedRoute>
  );
}

The SDK keeps verification UI inside the product site. Render your own branded notice when needsEmailVerification is true:

const { needsEmailVerification, requestEmailVerification, refreshUser } = useAuth();

if (needsEmailVerification) {
  // Render the product's own verification notice.
}

await requestEmailVerification({ returnTo: window.location.href });
await refreshUser();

The API resolves the recipient and organization from the authenticated token. Return URLs are restricted to the organization's configured member site.

3b. Add privacy-safe analytics

Analytics is optional and provider-independent. AxCloud resolves the public tracker configuration from the organization, site key, deployment stage, and current hostname. SDK consumers never receive the Umami API key.

import { AxCloudProvider, useAnalytics } from '@axcode/axcloud-sdk';

<AxCloudProvider
  orgId="org_ba81807b"
  apiUrl="https://api.axcloud.app"
  stage="prod"
  analytics={{
    enabled: analyticsConsent === 'accepted',
    siteKey: 'member-site',
    normalizePath: (path) => path.replace(/^\/member\/news\/[^/]+/, '/member/news/:id'),
  }}
  auth0={auth0Config}
>
  <App />
</AxCloudProvider>;

The SDK tracks SPA page changes after the tracker is ready. Query strings and fragments are removed, common opaque IDs are replaced with :id, Do Not Track is honored, and localhost collection is disabled unless trackLocalhost: true is explicitly set.

Custom events must be allowlisted in the AxCloud analytics site configuration:

function NewsArticle() {
  const analytics = useAnalytics();

  const handleOpen = () => {
    analytics.track('news_opened', { surface: 'member_news' });
  };
}

Only send coarse enums, booleans, and counts. The SDK rejects sensitive property names and email-like values, but applications remain responsible for never supplying names, email addresses, user IDs, document titles, search text, chat text, or other personal/content data. Production trackers should only be enabled after the product's privacy notice and Content Security Policy have been reviewed.

4. Use sections

import { useMySection } from '@axcode/axcloud-sdk';

function MySectionPage() {
  const { section, members, isLoading } = useMySection();

  if (isLoading) return <Spinner />;
  if (!section) return <p>You are not assigned to a section</p>;

  return (
    <div>
      <h1>{section.name}</h1>
      <h2>Members ({members.length})</h2>
      <ul>
        {members.map((m) => (
          <li key={m.id}>{m.name}</li>
        ))}
      </ul>
    </div>
  );
}

4b. Section-scoped content

import { useNews, useMySection } from '@axcode/axcloud-sdk';

function SectionNews() {
  const { section } = useMySection();
  // AxCloud derives the allowed sections from the authenticated membership.
  const { data: visibleNews } = useNews();

  return (
    <div>
      <h2>Section News</h2>
      {visibleNews.map((item) => (
        <NewsCard key={item.id} item={item} />
      ))}
      {!section && <p>You are not assigned to a section.</p>}
    </div>
  );
}

5. Fetch organization branding and media

import { useBranding, useLogo, useMedia } from '@axcode/axcloud-sdk';

function Header() {
  const { logoUrl, branding, organizationName } = useBranding();

  return (
    <header style={{ backgroundColor: branding?.primaryColor }}>
      {logoUrl && <img src={logoUrl} alt={organizationName} />}
      <h1>{organizationName}</h1>
    </header>
  );
}

// Or just the logo
function Logo() {
  const { logoUrl, isLoading } = useLogo();

  if (isLoading) return <Skeleton />;
  return logoUrl ? <img src={logoUrl} alt="Logo" /> : <DefaultLogo />;
}

// Fetch specific media file
function HeroImage() {
  const { mediaUrl } = useMedia('hero-banner.jpg');
  return mediaUrl ? <img src={mediaUrl} alt="Hero" /> : null;
}

Note: Media URLs are signed S3 URLs that expire after 1 hour. The SDK automatically refreshes them before expiry (staleTime: 30 min).

6. Fetch public documents

import { useDocuments, useDocumentDownload } from '@axcode/axcloud-sdk';

function PublicDocuments() {
  const { documents, isLoading } = useDocuments({ tags: ['handbook'], limit: 20 });

  if (isLoading) return <Skeleton />;
  return (
    <ul>
      {documents.map((doc) => (
        <li key={doc.document_id}>
          <DocumentLink documentId={doc.document_id} title={doc.title} />
        </li>
      ))}
    </ul>
  );
}

function DocumentLink({ documentId, title }: { documentId: string; title: string }) {
  const { documentUrl } = useDocumentDownload(documentId);
  return documentUrl ? <a href={documentUrl}>{title}</a> : <span>{title}</span>;
}

Only documents with status: "published" and audience: "public" are exposed through SDK document hooks.

7. Gate features by module

import { ModuleGate, useOrg } from '@axcode/axcloud-sdk';

function Dashboard() {
  const { hasModule } = useOrg();

  return (
    <div>
      <NewsSection />

      <ModuleGate module="training" fallback={<UpgradePrompt />}>
        <TrainingSection />
      </ModuleGate>

      {hasModule('ai') && <AIChatWidget />}
    </div>
  );
}

8. Membership applications with a section choice

import { createFormHelper } from '@axcode/axcloud-sdk';

const forms = createFormHelper({
  apiUrl: 'https://api.axcloud.app/forms',
  apiKey: import.meta.env.VITE_AXCLOUD_FORMS_API_KEY, // forms:submit only
  site: 'example.org',
});

// Sections the organization ticked as selectable, e.g. [{ id: 'section_x', name: 'SAS' }]
const sections = await forms.getMembershipSections();

// Either the chosen section…
await forms.submitMembership(email, { firstName, lastName, sectionId: sections[0].id });
// …or a company without a section of its own.
await forms.submitMembership(email, { firstName, lastName, company: 'Example Air' });

The API refuses a sectionId that is not in the list. The approval in the console invites the applicant into the chosen section. getMembershipSections throws when the API refuses (for example when the membership form is not enabled), so a site can fall back to a free-text company field. The call is checked with the forms key like a submission but does not count toward the key's rate limit, and browsers may keep the answer for five minutes.

API Reference

Provider

ComponentDescription
AxCloudProviderMain provider that combines Auth0, React Query, and SDK context

Hooks

HookDescription
useAuth()Login/logout, email-verification state, resend, and profile refresh
useUser()Fetch the normalized user and verified organization membership
useUpdateMyProfile()Update the signed-in user's name, phone, and address
useOrg()Fetch organization info and check modules
useNews(options?)Fetch the published news collection visible to the current visitor
useContent(type, options?)Fetch a published news, pages, or events collection
useContentItem(type, id, opts?)Fetch one published item directly by ID or slug
usePageContent(slug, options?)Fetch a page and read structured template slots
useMySection()Fetch current user's section with members
useSections()Fetch all organization sections
useBranding()Fetch organization branding (logo, colors)
useLogo()Fetch organization logo URL
useMedia(mediaId)Fetch specific media file URL
useAvatar(userId)Fetch user avatar URL
useMediaList(type)List media files
useDocuments(options?)List published public documents
useDocumentDownload(documentId)Fetch a signed document download URL

Profile self-service is deliberately limited to personal contact fields. Organization title, email, roles, status, organization, and section assignments remain administrator-managed.

useUser() normalizes the API response to camelCase. Organization-specific values always come from the membership for the provider's orgId:

const {
  user,
  roleInOrg,
  title,
  section, // { id, name, role } | null
  sections, // all verified section memberships
  sectionId,
  sectionIds,
  memberSince, // normalized from joined_at
} = useUser();

return (
  <p>
    {section?.name ?? 'No section'} · member since {memberSince}
  </p>
);
import { useUpdateMyProfile } from '@axcode/axcloud-sdk';

function ProfileForm() {
  const updateProfile = useUpdateMyProfile();

  const save = () =>
    updateProfile.mutateAsync({
      firstName: 'Anna',
      lastName: 'Pilot',
      phone: '+46701234567',
      address: 'Flyggatan 1',
    });

  return (
    <button onClick={save} disabled={updateProfile.isPending}>
      Save profile
    </button>
  );
}

Content Query Options

interface ContentQueryOptions {
  page?: number; // Page number (default: 1)
  pageSize?: number; // Items per page (default: 20)
  search?: string; // Search in title/content
  sortBy?: 'publishedAt' | 'createdAt' | 'updatedAt' | 'title';
  sortOrder?: 'asc' | 'desc';
  tags?: string[];
  lang?: string; // Return translated title/slug/blocks when available
  publicOnly?: boolean; // Force public endpoint even when authenticated
}

Collection hooks return data, count, total, page, pageSize, hasMore, nextCursor, isLoading, isFetching, isError, error and refetch. During Auth0's initial session check, member-capable hooks wait instead of briefly requesting public content. Member caches are isolated per Auth0 subject and removed on logout or account change. All other identity-bound AxCloud query data is removed at the same boundary.

Structured slots are untrusted CMS input. Prefer text(), list() and image(). unsafeHtml() (and the deprecated html() alias) must be sanitized before use with dangerouslySetInnerHTML.

Components

ComponentDescription
<LoginButton>Trigger Auth0 login
<LogoutButton>Trigger logout
<ProtectedRoute>Require authentication to view content
<ModuleGate>Conditionally render based on enabled modules

Advanced Usage

Custom API calls

import { useAuth } from '@axcode/axcloud-sdk';

function MyComponent() {
  const { getAccessToken } = useAuth();

  const fetchCustomData = async () => {
    const token = await getAccessToken();
    const response = await fetch('/api/custom', {
      headers: { Authorization: `Bearer ${token}` },
    });
    return response.json();
  };
}

Access the underlying Auth0 client

const { getAuth0Client } = useAuth();
const auth0 = getAuth0Client();

// Full Auth0 API available
await auth0.getAccessTokenSilently();
await auth0.getIdTokenClaims();

Security Model

API Key vs Authentication Token

The SDK uses two types of credentials:

  1. API Key (apiKey prop): A public identifier for your organization

    • Safe to include in client-side code
    • Used for public content endpoints
    • Backend validates and rate-limits based on API key
    • Without a valid API key, requests return 401
  2. Authentication Token (via Auth0): A secret JWT token

    • Automatically managed by the SDK
    • Required for member-only content and user data
    • Never exposed in client code

What the API Key Protects

Without API KeyWith API KeyWith Auth Token
NothingPublic news/contentMember content
Organization brandingUser profile
Public mediaLinked accounts
Admin functions

Endpoint Security

Endpoint TypeAuthenticationExample
Public contentAPI key (x-api-key header)/content/public/{orgId}/news
Member contentJWT Bearer token/content/members/{orgId}/news
User dataJWT Bearer token/api/users/me
AdminJWT Bearer token + role/forms/admin/submissions

Error Handling

import { useNews, isAxCloudError } from '@axcode/axcloud-sdk';

function NewsPage() {
  const { data, error, isError } = useNews();

  if (isError) {
    if (isAxCloudError(error)) {
      switch (error.code) {
        case 'UNAUTHORIZED':
          return <LoginPrompt />;
        case 'NOT_FOUND':
          return <NotFound />;
        default:
          return <Error message={error.message} />;
      }
    }
    return <Error message="Something went wrong" />;
  }

  return <NewsList items={data} />;
}

License

UNLICENSED - Internal use only. Contact Axcode AB for access.

版本与发布记录

@axcode/axcloud-sdk 共发布 49 个版本,首次发布于 2026/02/02;过去 12 个月发布了 49 个版本。

版本发布日期距今
1.0.0latest2026/11/026 天前
0.4.0-dev.88ecf17next2026/11/017 天前
0.4.0-dev.dc40cc82026/11/017 天前
0.4.0-dev.c93de932026/11/017 天前
0.5.02026/10/2315 天前
0.4.0-dev.f9603ab2026/10/2316 天前

关于 @axcode/axcloud-sdk 的常见问题

@axcode/axcloud-sdk 是什么?

SDK for building websites with AxCloud CMS and member management

如何安装 @axcode/axcloud-sdk?

在项目目录运行 npm install @axcode/axcloud-sdk,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^18.0.0 || ^19.0.0)、react-dom(^18.0.0 || ^19.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@axcode/axcloud-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@axcode/axcloud-sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@axcode/axcloud-sdk 可以商用吗?

@axcode/axcloud-sdk 使用 SEE LICENSE IN LICENSE.md 许可。许可条款写在包内的独立文件中,请阅读该文件确认可否商用。以上为概括,不构成法律意见,请以许可原文为准。

@axcode/axcloud-sdk 的下载量有多少?

根据 npm 官方下载统计,@axcode/axcloud-sdk 在 2026/10/08 – 2026/11/06 这 30 天内被下载 1,837 次,日均约 61 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@axcode/axcloud-sdk 还在维护吗?

最新版本 1.0.0 发布于 2026/11/02(6 天前);至今共发布 49 个版本,其中过去 12 个月发布了 49 个;首次发布于 2026/02/02。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。