A comprehensive JavaScript SDK for communicating with Apito GraphQL API endpoints. This SDK provides both type-safe and flexible interfaces for interacting with Apito's backend services.
- β Complete SDK Implementation: Full implementation matching the Go SDK
- β Type-Safe Operations: Generic typed methods for better development experience
- β GraphQL-Based: Native GraphQL communication with Apito backend
- β Authentication Ready: API key and tenant-based authentication
- β Promise-Based: Modern async/await support
- β Comprehensive Error Handling: Detailed error responses and GraphQL error support
- β Plugin-Ready: Perfect for Node.js applications and microservices
- β Production Ready: Battle-tested patterns and error handling
npm install @apito-io/js-admin-sdkor
yarn add @apito-io/js-admin-sdkAPI routes and middleware run in workerd, which has no Node require. Use the SDK as ESM only:
import { ApitoClient } from '@apito-io/js-admin-sdk';From v3.2.0, dist/index.mjs bundles axios for browser/worker runtimes so you do not pull axiosβs Node CJS build (require is not defined).
Tips if a bundler still resolves the wrong build:
- Do not add
@apito-io/js-admin-sdktovite.ssr.noExternal(that can force the CJS entry). - Optional Vite aliases in
astro.config.mjs:
import path from 'node:path';
export default defineConfig({
vite: {
resolve: {
alias: {
'@apito-io/js-admin-sdk': path.resolve(
'node_modules/@apito-io/js-admin-sdk/dist/index.mjs'
),
},
},
optimizeDeps: { exclude: ['@apito-io/js-admin-sdk'] },
},
});Node scripts can still use require('@apito-io/js-admin-sdk') (CJS build, axios as peer dependency).
import { ApitoClient } from '@apito-io/js-admin-sdk';
// Create a new client
const client = new ApitoClient({
baseURL: 'https://api.apito.io/graphql',
apiKey: 'your-api-key-here',
timeout: 30000,
});
// Create a new todo
async function createTodo() {
const todoData = {
title: 'Learn Apito SDK',
description: 'Complete the SDK tutorial',
status: 'todo',
priority: 'high',
};
const request = {
model: 'todos',
payload: todoData,
};
const todo = await client.createNewResource(request);
console.log('Created todo:', todo.id);
}const client = new ApitoClient({
baseURL: 'https://api.apito.io/graphql', // Your Apito GraphQL endpoint
apiKey: 'your-api-key-here', // X-APITO-KEY header value
timeout: 30000, // Request timeout in milliseconds
tenantId: 'your-tenant-id', // Optional tenant ID
httpClient: { // Optional axios configuration
maxRedirects: 5,
// ... other axios config
},
});Get a single resource by model and ID.
const todo = await client.getSingleResource('todos', '123');
console.log(todo.data.title);Search resources in a model with filtering. Only these filter fields are sent to GraphQL (same as the Go internal SDK): _key, page, limit, where, search. The aggregate argument is reserved for a future schema option and is not sent today.
const results = await client.searchResources('todos', {
where: { status: 'todo' },
limit: 10,
page: 1,
});
console.log(`Found ${results.count} todos`);Create a new resource.
const newTodo = await client.createNewResource({
model: 'todos',
payload: {
title: 'New Task',
status: 'todo',
},
connect: { user: 'user-123' }, // Optional relations
});Update an existing resource.
const updatedTodo = await client.updateResource({
model: 'todos',
id: '123',
payload: {
status: 'completed',
completed_at: new Date().toISOString(),
},
connect: { tags: ['urgent'] }, // Add relations
disconnect: { tags: ['low-priority'] }, // Remove relations
});Delete a resource.
await client.deleteResource('todos', '123');Get related documents.
const relatedUsers = await client.getRelationDocuments('todo-123', {
model: 'users',
field: 'assigned_to',
});These calls use the admin client and system GraphQL endpoint. They mirror the Go SDK. Pass projectId; on Pro/SaaS engines each user may include tenant_id.
| Method | Description |
|---|---|
searchUsers(projectId, limit?, offset?, tenantId?) |
List project end-users. Pro SaaS: pass tenantId to filter by catalog tenant. |
searchTenantsByDomain(projectId, domain) |
Exact domain lookup in project scope; returns { tenant } (null if no match). |
createUser(projectId, params) |
Create a local-password user; params: { password, role?, email?, phone?, tenantId? }. Pro SaaS: pass tenantId so the user is created under the correct catalog tenant (omit β engine may default to first active tenant). |
loginUser(params) |
General: { projectId, password, email? or phone? }. SaaS per-tenant DB: tenantId required. Google OAuth: googleOAuthState(projectId) then loginUser({ projectId, authMethod: 'google', code, state }). Native mobile: loginUser({ projectId, authMethod: 'google_id_token', idToken }). Google login may link a verified email to an existing user instead of creating a duplicate. |
googleOAuthState(projectId) |
Returns { state } for the Google authorize URL. |
updateUser(userId, params) |
Mutate email, phone, role, and/or tenantId (pro SaaS scope). Engine rejects duplicate email/phone project-wide when applicable. |
resetUserPassword(userId, password) |
Admin password reset. |
deleteUser(userId) |
Remove a project user. |
REST base is derived from baseURL by stripping /graphql (when GraphQL is /system/graphql, defaults to /secured), or set restBaseURL explicitly (e.g. http://host:5050/secured).
File metadata is stored in the project database files table (not the system DB). On Pro/SaaS engines, pass tenantId on the client or X-Apito-Tenant-ID so list/upload/delete target the tenant project DB. Default restBaseURL resolves to /secured when GraphQL uses /system/graphql; full URLs are /secured/files/upload, /secured/files/list, and /secured/files/delete.
| Method | Description |
|---|---|
uploadFile(params) |
POST /files/upload (multipart). |
listFiles(fileType?, limit?, offset?) |
GET /files/list. |
deleteFiles(ids) |
POST /files/delete. |
Path constants: FILES_UPLOAD_PATH, FILES_LIST_PATH, FILES_DELETE_PATH.
On the engine system GraphQL API, createTenant accepts an optional domain; when set, the domain must be unused in the project (otherwise the mutation fails). updateTenant enforces the same when setting domain to a non-empty value. Call those mutations via executeGraphQL if needed.
const projectId = 'your-project-id';
const { users, count } = await client.searchUsers(projectId, 50, 0);
console.log(
'users:',
count,
users.map((u) => u.email || u.phone || u.id),
);
const login = await client.loginUser({
projectId,
password: 'your-password',
email: 'user@example.com', // use phone: '+15551234567' when project is phone mode
});
if (login.token) {
console.log('tenant-scoped token:', login.token);
}Runnable samples: examples/users, examples/files (set APITO_BASE_URL, APITO_API_KEY, APITO_PROJECT_ID).
For type-safe operations, use the TypedOperations class:
import { TypedOperations } from '@apito-io/js-admin-sdk';
const typed = new TypedOperations(client);
// Define your types
interface Todo {
id: string;
title: string;
description: string;
status: 'todo' | 'in_progress' | 'completed';
priority: 'low' | 'medium' | 'high';
}
// Type-safe operations
const typedTodo = await typed.createNewResourceTyped<Todo>({
model: 'todos',
payload: {
title: 'Type-safe todo',
status: 'todo',
priority: 'high',
},
});
// TypeScript will infer the correct type
console.log(typedTodo.data.title); // string
console.log(typedTodo.data.status); // 'todo' | 'in_progress' | 'completed'The SDK provides comprehensive error handling:
import { ApitoError, ValidationError, GraphQLError } from '@apito-io/js-admin-sdk';
try {
const result = await client.getSingleResource('todos', 'invalid-id');
} catch (error) {
if (error instanceof ValidationError) {
console.error('Validation error:', error.message);
} else if (error instanceof GraphQLError) {
console.error('GraphQL error:', error.graphQLErrors);
} else if (error instanceof ApitoError) {
console.error('API error:', error.statusCode, error.message);
} else {
console.error('Unknown error:', error);
}
}You can configure the client using environment variables:
# Required
APITO_BASE_URL=https://api.apito.io/graphql
APITO_API_KEY=your-production-api-key
# Optional
APITO_TENANT_ID=your-tenant-id
APITO_TIMEOUT=30000import { ApitoClient } from '@apito-io/js-admin-sdk';
const client = new ApitoClient({
baseURL: process.env.APITO_BASE_URL,
apiKey: process.env.APITO_API_KEY,
tenantId: process.env.APITO_TENANT_ID,
timeout: parseInt(process.env.APITO_TIMEOUT || '30000'),
});import { ApitoClient } from '@apito-io/js-admin-sdk';
const client = new ApitoClient({
baseURL: 'https://api.apito.io/graphql',
apiKey: 'your-api-key',
});
// Create
const user = await client.createNewResource({
model: 'users',
payload: {
name: 'John Doe',
email: 'john@example.com',
},
});
// Read
const fetchedUser = await client.getSingleResource('users', user.id);
// Update
const updatedUser = await client.updateResource({
model: 'users',
id: user.id,
payload: {
name: 'John Updated',
},
});
// Delete
await client.deleteResource('users', user.id);// Complex search with multiple filters
const results = await client.searchResources('products', {
where: {
AND: [
{ price: { gte: 100 } },
{ category: { in: ['electronics', 'books'] } },
{ status: 'active' },
],
},
search: 'laptop',
limit: 20,
page: 1,
});// Create multiple records
const todos = [
{ title: 'Task 1', status: 'todo' },
{ title: 'Task 2', status: 'todo' },
{ title: 'Task 3', status: 'todo' },
];
const createdTodos = await Promise.all(
todos.map(todo => client.createNewResource({
model: 'todos',
payload: todo,
}))
);This client mirrors the Go go-internal-sdk package and the InternalSDKOperation interface from github.com/apito-io/types.
- Tenant header: In Go, set
context.WithValue(ctx, "tenant_id", id)before calls. In JavaScript, settenantIdonClientConfig, or pass{ tenantId }toexecuteGraphQLoptions where relevant. generateTenantToken(tenantId, duration?, role?): Matches enginegenerateTenantTokenβtenant_id,duration(YYYY-MM-DD; omit for default one year ahead in UTC), optionalrole(omit for engine defaultadmin). SendsX-Apito-Tenant-IDfor the mutation. Auth uses the clientapiKey.- GraphQL errors: The Go client returns
(response, err)when the response includeserrors. This SDK throwsGraphQLErrorwithgraphQLErrorsand the full payload onresponse; useerror.partialDatato readdatawhen the server returns partial success. searchResourcesfilter: Only_key,page,limit,where, andsearchare forwarded. Extra keys are ignored so unknown GraphQL variables cannot break the request.TypedOperations:datais deep-cloned viaJSON.parse(JSON.stringify(...)), matching the Go SDKβs marshal/unmarshal approach for typed documentdata.
The SDK can emit per-model GraphQL documents and TanStack Query v5 hooks from a checked-in engine introspection snapshot.
# 1. Refresh schema/apito_introspection.json from your engine (introspection query + X-Apito-Key)
# 2. Regenerate operations + types
npm run gen # gen:operations + gen:types
# or separately:
npm run gen:operations # β codegen/operations/*.graphql, codegen/schema.graphql
npm run gen:types # β src/generated/types.ts, sdk.ts, hooks.tsExports: getSdk, createApitoFetcher, useApitoFetcher, generated hooks, and naming helpers (apitoGraphQLComposedTypeName, DocumentBuilder, β¦) from the package root.
Optional peer: @tanstack/react-query ^5 (for generated hooks only; core ApitoClient does not require React).
See CONTRACT.md for the cross-SDK naming/operation contract shared with Flutter and Go admin SDKs.
git clone https://github.com/apito-io/js-admin-sdk.git
cd js-admin-sdk
npm install
npm run buildnpm testcd examples/basic
npm install
npm startPro tenant-user listing (optional APITO_TENANT_EMAIL / APITO_TENANT_PHONE + APITO_TENANT_PASSWORD for login):
cd examples/users
npm install
APITO_BASE_URL=http://localhost:5050/system/graphql APITO_API_KEY=... APITO_PROJECT_ID=... npm startThis project is licensed under the MIT License - see the LICENSE file for details.
- π§ Email: support@apito.io
- π¬ Discord: Join our community
- π Documentation: docs.apito.io
- π Bug Reports: GitHub Issues