npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-client

Quick 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 assertions

Service 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.ts

Step 2: Generate Types

Run the type generation command:

yarn gen:types

This 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

  1. Update API specifications (if needed, edit redocly.yaml)
  2. Run yarn gen:types to regenerate TypeScript definitions
  3. Review generated files in __generated__/ directories
  4. Update client implementation if the API surface changed
  5. 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