@apito-io/js-admin-sdk
v3.2.0
Published
Admin JavaScript SDK for Apito GraphQL API (mirrors go-internal-sdk)
Maintainers
Readme
Apito Admin JavaScript SDK
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.
🚀 Features
- ✅ 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
📦 Installation
npm install @apito-io/js-admin-sdkor
yarn add @apito-io/js-admin-sdkAstro / Cloudflare Workers
API 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).
🎯 Quick Start
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);
}📚 API Reference
Client Configuration
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
},
});Core Methods
getSingleResource(model, id, singlePageData?)
Get a single resource by model and ID.
const todo = await client.getSingleResource('todos', '123');
console.log(todo.data.title);searchResources(model, filter?, aggregate?)
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`);createNewResource(request)
Create a new resource.
const newTodo = await client.createNewResource({
model: 'todos',
payload: {
title: 'New Task',
status: 'todo',
},
connect: { user: 'user-123' }, // Optional relations
});updateResource(request)
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
});deleteResource(model, id)
Delete a resource.
await client.deleteResource('todos', '123');getRelationDocuments(id, connection)
Get related documents.
const relatedUsers = await client.getRelationDocuments('todo-123', {
model: 'users',
field: 'assigned_to',
});Project users (system GraphQL)
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?) | List project end-users (email, phone, optional tenant_id). |
| 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? }. |
| loginUser(params) | General: { projectId, password, email? or phone? }. Google: googleOAuthState(projectId) then loginUser({ projectId, authMethod: 'google', code, state }). |
| googleOAuthState(projectId) | Returns { state } for the Google authorize URL. |
| updateUser(userId, params) | Mutate email, phone, and/or role only. |
| resetUserPassword(userId, password) | Admin password reset. |
| deleteUser(userId) | Remove a project user. |
Files (REST)
REST base is derived from baseURL by stripping /graphql, or set restBaseURL on the client config.
| Method | Description |
|--------|-------------|
| uploadFile(params) | POST /files/upload (multipart). |
| listFiles(fileType?, limit?, offset?) | GET /files/list. |
| deleteFiles(ids) | POST /files/delete. |
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: '[email protected]', // 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).
Typed Operations
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'Error Handling
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);
}
}Environment Variables
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'),
});🧪 Examples
Basic CRUD Operations
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: '[email protected]',
},
});
// 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);Advanced Filtering
// 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,
});Batch Operations
// 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,
}))
);Parity with the Go internal SDK
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.
🏗️ Development
Building from Source
git clone https://github.com/apito-io/js-admin-sdk.git
cd js-admin-sdk
npm install
npm run buildRunning Tests
npm testRunning Examples
cd 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 start📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🔗 Links
🆘 Support
- 📧 Email: [email protected]
- 💬 Discord: Join our community
- 📖 Documentation: docs.apito.io
- 🐛 Bug Reports: GitHub Issues
