@keboola/api-client
v0.1.0
Published
TypeScript SDK for Keboola APIs
Readme
@keboola/api-client
TypeScript SDK for Keboola APIs. Provides typed clients for all Keboola platform services.
Installation
npm install @keboola/api-clientQuick Start
Full client initialization (browser)
import { createApiClient } from '@keboola/api-client';
const api = createApiClient();
await api.init({
baseUrl: 'https://connection.keboola.com',
token: {
storageApi: 'your-storage-token',
managementApi: 'your-management-token', // optional
},
assetsBaseUrl: 'https://assets.keboola.com',
statusBaseUrl: 'https://status.keboola.com',
metastoreBaseUrl: 'https://metastore.keboola.com',
callbacks: {
onError: (error) => console.error('API Error:', error),
onSuccess: () => {},
onSettled: () => {},
},
});
const tables = await api.storage.tables.list();
const jobs = await api.queue.searchJobs();Individual client (Node.js)
import { createStorageClient, createStorageTokenMiddleware } from '@keboola/api-client/storage';
const tokenMiddleware = createStorageTokenMiddleware('your-storage-token');
const storage = createStorageClient({
baseUrl: 'https://connection.keboola.com',
middlewares: [tokenMiddleware],
});
const stackInfo = await storage.getStackInfo();
const files = await storage.files.list();Type imports
import type { ApiClientOptions, ApiClient } from '@keboola/api-client';
import type { StackInfo, ServiceId } from '@keboola/api-client/storage/types';
import type { GetQueueJobPath } from '@keboola/api-client/queue/types';
import type { VariableWithHash } from '@keboola/api-client/vault/types';Architecture
src/
client.ts # createApiClient() factory — top-level entry
devClient.ts # Lightweight dev/verify client
constants.ts # HTTP headers, prefixes
fetchClient/
createFetchClient/ # Core fetch wrapper, middleware composition
createGenericFetchClient.ts # Untyped HTTP client (manual generics)
createOpenapiFetchClient.ts # Typed HTTP client (from OpenAPI schemas)
types.ts # OpenAPI type-level helpers
clients/
storage/ # Storage API (buckets, tables, files, branches, ...)
management/ # Management API (projects, features, users)
vault/ # Vault secrets
dataScience/ # Data apps, sandboxes, runtimes
editor/ # SQL sessions, queries
encryption/ # Value/secret encryption
chat/ # Kai Assistant chat
kaiAgent/ # Kai Agent (agentic assistant)
ai/ # AI describe/explain/suggest
queue/ # Job Queue
queryService/ # SQL query service
syncActions/ # Sync actions, MFA, git repos
telemetry/ # Telemetry provisioning
metastore/ # Metastore repository/schema
assets/ # Platform changelog
status/ # Status page summary
import/ # File upload
verify/ # Token verification
domain/
stack.ts # Stack info, service discovery, feature flags
project.ts # Project info, backend/feature checks
sdks/
tag/ # High-level tag CRUD (composes metastore client)
errors/
ApiError.ts # HTTP error with response/request/data
UserError.ts # User-facing validation error
ServiceUnavailableError.ts # Service not available in current stack
ManagementClientAuthError.ts
utils/
concurrent.ts # Concurrent task runner with middleware
series.ts # Sequential task runner
poll.ts # Polling with isDone/isFailed predicates
delay.ts # Abortable delay
keboolaUID.ts # Keboola object ID serialization
generateUUID.ts # crypto.randomUUID wrapper
assert.ts # Runtime assertionsService Clients
After calling api.init(...), the following clients are available on the api object.
Clients backed by OpenAPI specs are marked with (OpenAPI); the rest use the generic fetch client.
| Accessor | Service | Key methods |
| ------------------ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| api.storage | Storage API | buckets.*, tables.*, files.*, branches.*, workspaces.*, mergeRequests.*, jobs.*, componentsAndConfigurations.*, tokens.*, getStackInfo() |
| api.management | Management API | projects.*, features.*, users.* |
| api.vault | Vault (OpenAPI) | getVariables(), createVariable(), deleteVariable(), getVariablesByBranchId(), getProjectWideVariables() |
| api.dataScience | Data Science (OpenAPI) | getApps(), getApp(), createApp(), patchApp(), deleteApp(), getAppRuns(), getAppRun(), getAppLogsTail(), getAppLogsDownload(), getRuntimes(), getSandbox() |
| api.editor | SQL Editor (OpenAPI) | createSession(), getSession(), getSessions(), getSessionSchema(), createQueryJob(), tablePreview(), tableDefinition(), load(), unload() |
| api.encryption | Encryption | encrypt(), encryptSecrets() |
| api.chat | Kai Assistant (OpenAPI) | getHistory(), getChat(), createChat(), deleteChat(), getVotes(), submitVote(), getUsage(), getSuggestions(), getAgentSettings(), getToolsList() |
| api.kaiAgent | Kai Agent (OpenAPI) | getHistory(), getChat(), createChat(), deleteChat(), approveToolCall(), submitToolOutput(), getSuggestions(), getAgentSettings() |
| api.ai | AI Service (OpenAPI) | describeConfiguration(), describeConfigurationVersion(), describeConfigurationMerge(), explainError(), suggestComponent(), feedback() |
| api.queue | Job Queue (OpenAPI) | getJob(), searchJobs() |
| api.queryService | Query Service (OpenAPI) | createQueryJob(), cancelQueryJob(), getQueryJob(), getQueryResults(), getQueryHistory(), exportResults() |
| api.syncActions | Sync Actions (OpenAPI) | enrollMFA(), gitRepository.* |
| api.telemetry | Telemetry | provisioning.* |
| api.assets | Assets | getPublishedChangelogPosts() |
| api.status | Status | getSummary() |
| api.import | Import | uploadFile() |
Additional accessors on the initialized client:
| Accessor | Purpose |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| api.stack | Stack info: hasFeature(), hasService(), getServiceUrl(), hasComponent(), getComponent(), services, features, components |
| api.project | Project info: hasFeature(), hasAdminFeature(), hasBackend(), sapiToken |
| api.sdk.tag | Tag SDK: getTags(), getTagsByObjectId(), createTag(), updateTag(), deleteTag(), assignTags(), unassignTags(), createAndAssignTag() |
| api.serviceMeta | Service availability metadata from the registry |
Entry Points
// Main entry — full ApiClient + all exports
import { createApiClient, isApiError, concurrent, poll } from '@keboola/api-client';
// Per-client imports — individual client factories
import { createStorageClient } from '@keboola/api-client/storage';
import { createVerifyClient } from '@keboola/api-client/verify';
// Per-service type imports (tree-shakeable)
import type { StackInfo } from '@keboola/api-client/storage/types';
import type { GetQueueJobPath } from '@keboola/api-client/queue/types';Error Handling
import {
isApiError,
isFetchClientError,
isAbortError,
ApiError,
UserError,
ServiceUnavailableError,
} from '@keboola/api-client';
try {
await api.storage.tables.list();
} catch (error) {
if (isApiError(error)) {
// HTTP error — has response, request, and parsed data
console.error(error.response.status, error.data);
} else if (isFetchClientError(error)) {
// Network-level error
console.error(error.message);
} else if (isAbortError(error)) {
// Request was aborted via AbortSignal
} else if (error instanceof ServiceUnavailableError) {
// Service not available in the current stack
} else if (error instanceof UserError) {
// Validation error safe to show to end users
}
}Middleware
Middleware functions wrap every HTTP request. They execute in registration order (first registered = outermost).
import type { MiddlewareFn } from '@keboola/api-client';
const loggingMiddleware: MiddlewareFn = (next) => async (request) => {
console.log('Request:', request.url);
const response = await next(request);
console.log('Response:', response.response.status);
return response;
};Callback middleware
The built-in callback middleware provides onError, onSuccess, and onSettled hooks:
import { createApiClient } from '@keboola/api-client';
const api = createApiClient();
await api.init({
baseUrl: 'https://connection.keboola.com',
token: { storageApi: 'your-token' },
assetsBaseUrl: 'https://assets.keboola.com',
statusBaseUrl: 'https://status.keboola.com',
metastoreBaseUrl: 'https://metastore.keboola.com',
callbacks: {
onError: (error) => {
/* handle error */
},
onSuccess: () => {
/* e.g. reset idle timer */
},
onSettled: () => {
/* e.g. hide loading indicator */
},
},
});Utilities
concurrent / series
import { concurrent, series } from '@keboola/api-client';
// Process items with concurrency limit (default: 2)
const results = await concurrent({
items: ['a', 'b', 'c', 'd'],
process: async (item) => fetchData(item),
concurrency: 3,
});
// Process items one at a time
const ordered = await series({
items: ['a', 'b', 'c'],
process: async (item) => fetchData(item),
});poll
import { poll, PollException } from '@keboola/api-client';
const controller = new AbortController();
try {
const { data, callCount } = await poll({
pollFn: (signal) => api.queue.getJob(jobId, signal),
isDone: (job) => job.status === 'success',
isFailed: (job) => job.status === 'error',
interval: 2000,
maxAttempts: 30,
abortSignal: controller.signal,
});
} catch (error) {
if (error instanceof PollException) {
console.error(error.message, error.result);
}
}keboolaUID
import { keboolaUID } from '@keboola/api-client';
const uid = keboolaUID.serialize({
type: 'table',
projectId: 123,
uid: 'in.c-main.users',
});
// "KID--123--table--in.c-main.users"
const parsed = keboolaUID.deserialize(uid);
// { type: 'table', projectId: 123, uid: 'in.c-main.users' }Other utilities
import { delay, generateUUID, assert } from '@keboola/api-client';
// Abortable delay
await delay(1000, abortSignal);
// crypto.randomUUID wrapper
const id = generateUUID();
// Runtime assertion (throws if falsy)
assert(value, 'value must be defined');React Bindings
React bindings (provider, hooks, query integration) are in the separate @keboola/api-client-react package.
import { ApiClientProvider, useApiClient } from '@keboola/api-client-react';License
MIT. See LICENSE.
Contributing
Internal guide for adding new service clients to this package.
Adding a New OpenAPI Client
Step 1: Register the API in redocly.yaml
Add your API specification to the redocly.yaml file:
apis:
# Existing APIs...
yourNewService:
root: https://your-service.keboola.com/docs/swagger.yaml
x-openapi-ts:
output: ./src/clients/yourNewService/__generated__/schema.d.tsStep 2: Generate Types
Run the type generation command:
yarn gen:typesThis will create the TypeScript definitions at the specified output path.
Step 3: Create the Client Implementation
Create src/clients/yourNewService/yourNewServiceClient.ts:
import { createOpenapiFetchClient } from '../../fetchClient';
import type { ClientInitOptions } from '../types';
import type { paths } from './__generated__/schema';
export const createYourNewServiceClient = ({ baseUrl, middlewares }: ClientInitOptions) => {
const client = createOpenapiFetchClient<paths>({
baseUrl,
middlewares,
});
const listItems = async (signal?: AbortSignal) => {
const { data } = await client.get('/items', {}, { signal });
return data;
};
return {
listItems,
};
};Step 4: Create the Types Export File
Create src/clients/yourNewService/types.ts to re-export all types:
export type * from './__generated__/schema';
export type { YourNewServiceClient } from './yourNewServiceClient';Step 5: Create the Index File
Create src/clients/yourNewService/index.ts:
export * from './yourNewServiceClient';Step 6: Add Type Export to package.json
Add the type export path to package.json exports and add the entry to tsup.config.ts:
{
"exports": {
"./types/yourNewService": {
"import": {
"types": "./dist/types/yourNewService.d.ts",
"default": "./dist/types/yourNewService.js"
},
"require": {
"types": "./dist/types/yourNewService.d.cts",
"default": "./dist/types/yourNewService.cjs"
}
}
}
}Step 7: Integrate with Main ApiClient
Update src/client.ts to register the client in the createServiceClientRegistry call:
import { createYourNewServiceClient } from './clients/yourNewService';
// Inside createClients, add to the clients array:
{
serviceId: 'your-service',
clientName: 'yourNewService',
clientFn: (serviceBaseUrl) =>
createYourNewServiceClient({ baseUrl: serviceBaseUrl, middlewares: commonMiddlewares }),
},Adding a New Generic Client
Follow the same steps but use createGenericFetchClient instead of createOpenapiFetchClient. No redocly.yaml registration or yarn gen:types step is needed. Define request/response types manually.
OpenAPI Type Generation Workflow
- Update API specifications (if needed, edit
redocly.yaml) - Run
yarn gen:typesto regenerate TypeScript definitions - Review generated files in
__generated__/directories - Update client implementation if the API surface changed
- Always commit generated files to version control
redocly.yaml Configuration Reference
apis:
serviceName:
root: https://api.example.com/swagger.yaml
x-openapi-ts:
output: ./src/clients/serviceName/__generated__/schema.d.ts