Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ jobs:

- name: Run unit tests
run: npm run test:unit

- name: Verify built package entry points
run: npm run test:package

- name: Generate and validate platform reference
run: npm run docs:platform
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ You can use it in two ways:
- **Inside Base44 apps**: When Base44 generates your app, the SDK is already set up and ready to use.
- **External apps**: Use the SDK to build your own frontend or backend that uses Base44 as a backend service.

## Platform SDK (server)

The same package includes a separate server entry point for provisioning users and
managing their apps:

```ts
import { Base44PlatformClient } from "@base44/sdk/platform/server";

const platform = new Base44PlatformClient({ apiKey, workspaceId });
await platform.users.provision({ externalId: "customer_42" });
const apps = await platform.asUser("customer_42").apps.list();
```

Use this entry point only on your server. `tokenStore` is optional and defaults to
in-memory caching. See the [platform guide](platform-docs/README.md),
[complete API reference](platform-docs/api.md), and [token lifecycle](platform-docs/tokens.md).
The existing `@base44/sdk` root import continues to provide the runtime SDK.

## Installation

**Inside Base44 apps**: The SDK is already available. No installation needed.
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import tsParser from "@typescript-eslint/parser";

export default [
{
files: ["src/**/*.ts"],
files: ["src/**/*.ts", "platform-src/**/*.ts", "examples/platform-server.ts"],
languageOptions: {
parser: tsParser,
},
Expand Down
47 changes: 47 additions & 0 deletions examples/platform-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
Base44PlatformClient, Base44PlatformError,
type TokenKey, type TokenRecord, type TokenStore,
} from "@base44/sdk/platform/server";

/** Minimal setup: the default memory cache needs no configuration. */
export async function createForCustomer(apiKey: string, workspaceId: string) {
const platform = new Base44PlatformClient({ apiKey, workspaceId });
await platform.users.provision({ externalId: "customer_42", displayName: "Customer 42" });
const user = platform.asUser("customer_42");
const app = await user.apps.create({ name: "Tracker", prompt: "Build a project tracker" });
return user.apps.deploy(app.id);
}

/** Adapt your own server-side database; methods must protect credential records. */
interface CredentialDatabase {
read(key: string): Promise<TokenRecord | null>;
replace(key: string, record: TokenRecord): Promise<void>;
remove(key: string): Promise<void>;
}
export function persistentClient(apiKey: string, workspaceId: string, db: CredentialDatabase) {
const keyOf = (key: TokenKey) => JSON.stringify([key.serverUrl, key.workspaceId, key.externalId]);
const tokenStore: TokenStore = {
get: key => db.read(keyOf(key)),
set: (key, record) => db.replace(keyOf(key), record),
delete: key => db.remove(keyOf(key)),
};
return new Base44PlatformClient({ apiKey, workspaceId, tokenStore });
}

/** Authenticate your own user before passing their stable external ID. */
export async function listForCustomer(platform: Base44PlatformClient, externalId: string, signal: AbortSignal) {
try {
return await platform.asUser(externalId).apps.list({ limit: 20, skip: 0 }, { signal });
} catch (error) {
if (error instanceof Base44PlatformError) console.error(error.toJSON());
throw error;
}
}

export async function disconnect(platform: Base44PlatformClient, externalId: string) {
// Not offboarding: the principal remains and a later call can mint again.
await platform.asUser(externalId).revokeToken();
}
export async function offboard(platform: Base44PlatformClient, externalId: string) {
return platform.users.deprovision(externalId);
}
44 changes: 40 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"dist"
],
"scripts": {
"build": "tsc",
"lint": "eslint src",
"build": "npm run build:runtime && npm run build:platform",
"lint": "eslint src platform-src examples/platform-server.ts",
"test": "npm run test:types && vitest run",
"test:types": "tsc --noEmit -p tsconfig.type-tests.json",
"test:unit": "vitest run tests/unit",
Expand All @@ -23,7 +23,12 @@
"create-docs-local": "npm run create-docs && npm run copy-docs-local",
"copy-docs-local": "node scripts/mintlify-post-processing/copy-to-local-docs.js",
"create-docs:generate": "typedoc",
"create-docs:process": "node scripts/mintlify-post-processing/file-processing/file-processing.js"
"create-docs:process": "node scripts/mintlify-post-processing/file-processing/file-processing.js",
"build:runtime": "tsc",
"build:platform": "tsc -p tsconfig.platform.json",
"test:platform": "vitest run tests/unit/platform",
"test:package": "npm run build && node --test tests/package/platform-server.test.mjs",
"docs:platform": "typedoc --options typedoc.platform.json"
},
"dependencies": {
"axios": "^1.18.1",
Expand Down Expand Up @@ -63,5 +68,36 @@
"bugs": {
"url": "https://github.com/base44/javascript-sdk/issues"
},
"homepage": "https://github.com/base44/javascript-sdk#readme"
"homepage": "https://github.com/base44/javascript-sdk#readme",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./platform/server": {
"types": "./dist/platform/server/index.d.ts",
"default": "./dist/platform/server/index.js"
},
"./dist/*.d.ts": "./dist/*.d.ts",
"./dist/*.js": {
"types": "./dist/*.d.ts",
"default": "./dist/*.js"
},
"./dist/*": {
"types": "./dist/*.d.ts",
"default": "./dist/*.js"
},
"./package.json": "./package.json",
"./*": "./*"
},
"typesVersions": {
"*": {
"platform/server": [
"dist/platform/server/index.d.ts"
],
"*": [
"*"
]
}
}
}
90 changes: 90 additions & 0 deletions platform-docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Base44 Platform SDK

A server-side TypeScript client for provisioning users and managing the apps they build.
It complements [`@base44/sdk`](https://github.com/base44/javascript-sdk), which operates
inside an individual app: entities, functions, integrations and app-user authentication.

The platform client ships in the same `@base44/sdk` package, at the separate
`@base44/sdk/platform/server` entry point. It uses native `fetch` (Node 20+, or a
compatible server runtime). The platform entry point does not load the runtime SDK
or its dependencies.

## Start here

```ts
import { Base44PlatformClient } from "@base44/sdk/platform/server";

const platform = new Base44PlatformClient({
apiKey: process.env.BASE44_API_KEY!,
workspaceId: process.env.BASE44_WORKSPACE_ID!,
});

// Call at an explicit onboarding step, not automatically on every app request.
await platform.users.provision({ externalId: "customer_42", displayName: "Customer 42" });

// Authenticate your own incoming request before choosing this external ID.
const customer = platform.asUser("customer_42");
const app = await customer.apps.create({
name: "Project tracker",
prompt: "Build a project tracker",
publicSettings: "private_with_login",
});
await customer.apps.addToFolder("folder_id", [app.id]);
const deployment = await customer.apps.deploy(app.id);
console.log(deployment.appId, deployment.revision);
```

**One key.** The key must belong to `workspaceId` and have both
`service_users:provision` and `user_tokens:mint`. Base44 provisioning availability
also depends on your workspace's enabled capabilities. There is no separate
provisioning key setting in this SDK.

**Your users remain yours.** `externalId` is a stable identifier from your system,
not an email or a Base44 user ID. Base44 creates a synthetic service identity for
it. `asUser()` neither provisions an identity nor changes the root client's identity.
App calls lazily acquire that user's server credential and remain subject to Base44
app/workspace authorization. A workspace-wide list is not an ownership filter for
your product: enforce your product's own access rules on your server.

**Server credentials stay on the server.** Do not serialize the client, token store
or `getAccessToken()` result to the browser. A service access token is REST-capable.
This SDK does not implement a browser subscription token or change its permissions.
Runtime app credentials, preview credentials and service access tokens are different.

## Documentation

- [Complete API reference](api.md): every method, input, response and error.
- [Token storage and lifecycle](tokens.md): default memory store, persistent adapter
contract, renewal, revocation and offboarding.
- [Compilable examples](../examples/platform-server.ts): basic setup, storage integration, cancellation
and error handling. These functions are examples, not automatically executed scripts.

All app responses are projected at runtime. Unknown server fields are excluded; merely
asserting a TypeScript type on the raw response is not sufficient. Ordinary SDK errors
also exclude server response prose and request credentials. Chat and sockets are outside this entry point.

## Integrating with your server

Keep your database adapter, product ownership checks, secret-name allowlists and
folder configuration in your application. Pass a persistent `TokenStore` when
credentials must survive process restarts.

Creation, folder assignment and local ownership recording are separate operations.
If the latter steps fail, the app has still been created. Retry the failed step using
the returned app ID rather than blindly creating another app.

## Verify without live credentials

From the repository root, using installed dependencies:

```sh
npm run test:types
npm run test:platform
npm run test:package
npm run docs:platform
npm run lint
```

Tests use mocked HTTP responses and exercise contracts, identity isolation and the
published package entry points. Examples are typechecked; a documentation check covers public exports.
No build, deployment, token or database mutation is performed against a live service.
Loading
Loading