Skip to main content

Client SDK

Beta

This feature is in beta. Core behavior is stable and ready to try, but some APIs or configuration may still evolve before general availability.

Call App Functions from the client SDK with auth headers injected automatically.

client.functions is a hand-written helper surface. It is consistent across SDKs, but unlike the generated REST core, it is not produced directly from OpenAPI.

Supported Client SDKs

  • JavaScript (@edge-base/web)
  • React Native (@edge-base/react-native)
  • Dart / Flutter
  • Swift
  • Kotlin
  • Java
  • C#
  • C++

Setup

import { createClient } from '@edge-base/web';

const client = createClient('https://my-app.edgebase.fun');

JavaScript function calls can opt into a deadline that covers both response headers and JSON body consumption:

await client.functions.call('reports/generate', {
method: 'POST',
body: { reportId: 'report-1' },
timeoutMs: 20_000,
});

A deadline failure has error slug request-timeout. EdgeBase does not automatically replay timed-out mutations because the server may already have committed them; confirm state or use an application-level idempotency key before retrying. To apply one default to JSON API calls, pass requestTimeoutMs to createClient. It is intentionally opt-in so long-running functions keep their existing behavior.

Use an AbortController when the application supersedes a Function call, for example when a newer search replaces an older one:

const controller = new AbortController();

const result = client.functions.call('reports/search', {
method: 'GET',
query: { q: 'quarterly' },
signal: controller.signal,
});

controller.abort();
await result;

An already-aborted signal performs no request. Cancellation also stops an in-flight body read or retry wait and rejects with the caller's abort reason (normally an AbortError), rather than a normalized network error. A configured timeoutMs remains a separate request-timeout error when the deadline wins.

Basic Calls

const result = await client.functions.post('send-email', {
to: 'user@example.com',
subject: 'Welcome!',
});

const users = await client.functions.get('users');
await client.functions.delete('users/abc123');

Generic Call

const result = await client.functions.call('my-function', {
method: 'PUT',
body: { name: 'Updated' },
});

// For a function declared with captcha: true:
const protectedResult = await client.functions.call('submit-form', {
method: 'POST',
body: formData,
captchaToken,
});

For every client SDK above, captchaToken is a manually acquired Turnstile token for a Function declared with captcha: true. The Functions helper does not open the hosted challenge automatically; use your platform's hosted CAPTCHA integration with action function, then pass the returned token in FunctionCallOptions.

The SDK validates the token as a non-empty value of at most 2,048 characters and sends it only in X-EdgeBase-Captcha-Token. Turnstile tokens are single-use, so a CAPTCHA-protected Function call is never automatically replayed after a transport error or HTTP 401/429 response. Reconcile the operation with an application idempotency key or status read, acquire a new token, and retry explicitly.

Streaming And Binary Responses (JavaScript)

JavaScript clients can use callRaw() when a Function returns a ZIP, PDF, image, server-sent stream, or another non-JSON payload:

const response = await client.functions.callRaw('reports/archive', {
method: 'POST',
body: { reportId: 'report-1' },
query: { disposition: 'attachment' },
timeoutMs: 20_000,
});

if (!response.body) throw new Error('The archive response has no body.');
await response.body.pipeTo(downloadDestination);

callRaw() uses the same auth injection, one-time 401 refresh, locale and CAPTCHA headers, normalized non-success errors, rate-limit policy, and GET-versus-mutation replay rules as call(). A successful response is returned unchanged with its body unread; the SDK does not buffer, clone, or decode it.

For a raw call, timeoutMs covers the request until response headers are received. After the Response is returned, the application owns stream consumption, size limits, cancellation, and any later deadline. JSON calls keep their existing header-and-body deadline. Passing signal to callRaw() keeps the returned response body bound to that caller signal after header handoff. The raw-response helper is currently specific to the JavaScript SDKs; other SDKs retain their JSON Function helpers.

Authentication

If the user is signed in, the SDK sends the auth token automatically.

await client.auth.signIn({ email: 'user@test.com', password: 'pass123' });
const profile = await client.functions.get('me/profile');

Server function:

export const GET = defineFunction(async ({ auth, admin }) => {
if (!auth) throw new FunctionError('unauthenticated', 'Login required');
return admin.db('app').table('profiles').getOne(auth.id);
});

Error Handling

App Function failures are surfaced through the SDK as EdgeBaseError.

import { EdgeBaseError } from '@edge-base/web';

try {
await client.functions.post('orders', { items: [] });
} catch (err) {
if (err instanceof EdgeBaseError) {
if (err.status === 0) {
// The SDK did not receive a usable HTTP response.
retryLater();
return;
}

if (err.status === 401) {
redirectToLogin();
return;
}

showError(err.message);
}
}

Practical rules:

  • err.status and err.code are aliases for the HTTP status code.
  • status === 0 means a transport-level failure rather than a semantic App Function error response.
  • Server-side FunctionError('permission-denied', ...) becomes an SDK error with HTTP status 403, unauthenticated becomes 401, and so on.
  • For business-rule failures, branch on HTTP status. For retries, treat status === 0 and 503 as transient first.

Notes

  • React Native uses the same functions API shape as the web SDK.
  • C++ uses JSON strings for request bodies instead of language-level map serialization helpers.
  • Function routes are always resolved under /api/functions/*.