iceberg-js
    Preparing search index...

    iceberg-js

    iceberg-js

    CI npm version pkg.pr.new

    A small, framework-agnostic JavaScript/TypeScript client for the Apache Iceberg REST Catalog.

    • Generic: Works with any Iceberg REST Catalog implementation, not tied to any specific vendor
    • Minimal: Thin HTTP wrapper over the official REST API, no engine-specific logic
    • Type-safe: First-class TypeScript support with strongly-typed request/response models
    • Fetch-based: Uses native fetch API with support for custom implementations
    • Universal: Targets Node 20+ and modern browsers (ES2020)
    • Catalog-only: Focused on catalog operations (no data reading/Parquet support in v0.1.0)

    📚 Full API documentation: supabase.github.io/iceberg-js

    npm install iceberg-js
    
    import { IcebergRestCatalog } from 'iceberg-js'

    const catalog = new IcebergRestCatalog({
    baseUrl: 'https://my-catalog.example.com/iceberg/v1',
    auth: {
    type: 'bearer',
    token: process.env.ICEBERG_TOKEN,
    },
    })

    // Create a namespace
    await catalog.createNamespace({ namespace: ['analytics'] })

    // Create a table
    await catalog.createTable(
    { namespace: ['analytics'] },
    {
    name: 'events',
    schema: {
    type: 'struct',
    fields: [
    { id: 1, name: 'id', type: 'long', required: true },
    { id: 2, name: 'timestamp', type: 'timestamp', required: true },
    { id: 3, name: 'user_id', type: 'string', required: false },
    ],
    'schema-id': 0,
    'identifier-field-ids': [1],
    },
    'partition-spec': {
    'spec-id': 0,
    fields: [],
    },
    'write-order': {
    'order-id': 0,
    fields: [],
    },
    properties: {
    'write.format.default': 'parquet',
    },
    }
    )

    Creates a new catalog client instance.

    Options:

    • baseUrl (string, required): Base URL of the REST catalog
    • auth (AuthConfig, optional): Authentication configuration
    • catalogName (string, optional): Catalog name for multi-catalog servers. When specified, requests are sent to {baseUrl}/v1/{catalogName}/.... For example, with baseUrl: 'https://host.com' and catalogName: 'prod', requests go to https://host.com/v1/prod/namespaces
    • fetch (typeof fetch, optional): Custom fetch implementation
    • accessDelegation (AccessDelegation[], optional): Access delegation mechanisms to request from the server

    Authentication types:

    // No authentication
    { type: 'none' }

    // Bearer token
    { type: 'bearer', token: 'your-token' }

    // Custom header
    { type: 'header', name: 'X-Custom-Auth', value: 'secret' }

    // Custom function
    { type: 'custom', getHeaders: async () => ({ 'Authorization': 'Bearer ...' }) }

    Access Delegation:

    Access delegation allows the catalog server to provide temporary credentials or sign requests on your behalf:

    import { IcebergRestCatalog } from 'iceberg-js'

    const catalog = new IcebergRestCatalog({
    baseUrl: 'https://catalog.example.com/iceberg/v1',
    auth: { type: 'bearer', token: 'your-token' },
    // Request vended credentials for data access
    accessDelegation: ['vended-credentials'],
    })

    // The server may return temporary credentials in the table metadata
    const metadata = await catalog.loadTable({ namespace: ['analytics'], name: 'events' })
    // Use credentials from metadata.config to access table data files

    Supported delegation mechanisms:

    • vended-credentials: Server provides temporary credentials (e.g., AWS STS tokens) for accessing table data
    • remote-signing: Server signs data access requests on behalf of the client

    List all namespaces, optionally under a parent namespace.

    const namespaces = await catalog.listNamespaces()
    // [{ namespace: ['default'] }, { namespace: ['analytics'] }]

    const children = await catalog.listNamespaces({ namespace: ['analytics'] })
    // [{ namespace: ['analytics', 'prod'] }]

    Create a new namespace with optional properties.

    await catalog.createNamespace({ namespace: ['analytics'] }, { properties: { owner: 'data-team' } })
    

    Drop a namespace. The namespace must be empty.

    await catalog.dropNamespace({ namespace: ['analytics'] })
    

    Load namespace metadata and properties.

    const metadata = await catalog.loadNamespaceMetadata({ namespace: ['analytics'] })
    // { properties: { owner: 'data-team', ... } }

    List all tables in a namespace.

    const tables = await catalog.listTables({ namespace: ['analytics'] })
    // [{ namespace: ['analytics'], name: 'events' }]

    Create a new table.

    const metadata = await catalog.createTable(
    { namespace: ['analytics'] },
    {
    name: 'events',
    schema: {
    type: 'struct',
    fields: [
    { id: 1, name: 'id', type: 'long', required: true },
    { id: 2, name: 'timestamp', type: 'timestamp', required: true },
    ],
    'schema-id': 0,
    },
    'partition-spec': {
    'spec-id': 0,
    fields: [
    {
    source_id: 2,
    field_id: 1000,
    name: 'ts_day',
    transform: 'day',
    },
    ],
    },
    }
    )

    Load table metadata.

    const metadata = await catalog.loadTable({
    namespace: ['analytics'],
    name: 'events',
    })

    Update table metadata (schema, partition spec, or properties).

    const updated = await catalog.updateTable(
    { namespace: ['analytics'], name: 'events' },
    {
    properties: { 'read.split.target-size': '134217728' },
    }
    )

    Drop a table from the catalog.

    await catalog.dropTable({ namespace: ['analytics'], name: 'events' })
    

    All API errors throw an IcebergError with details from the server:

    import { IcebergError } from 'iceberg-js'

    try {
    await catalog.loadTable({ namespace: ['test'], name: 'missing' })
    } catch (error) {
    if (error instanceof IcebergError) {
    console.log(error.status) // 404
    console.log(error.icebergType) // 'NoSuchTableException'
    console.log(error.message) // 'Table does not exist'
    }
    }

    The library exports all relevant types:

    import type {
    NamespaceIdentifier,
    TableIdentifier,
    TableSchema,
    TableField,
    IcebergType,
    PartitionSpec,
    SortOrder,
    CreateTableRequest,
    TableMetadata,
    AuthConfig,
    AccessDelegation,
    } from 'iceberg-js'

    The following Iceberg primitive types are supported:

    • boolean, int, long, float, double
    • string, uuid, binary
    • date, time, timestamp, timestamptz
    • decimal(precision, scale), fixed(length)

    This package is built to work in all Node.js and JavaScript environments:

    Environment Module System Import Method Status
    Node.js ESM "type": "module" import { ... } from 'iceberg-js' ✅ Fully supported
    Node.js CommonJS Default const { ... } = require('iceberg-js') ✅ Fully supported
    TypeScript ESM module: "ESNext" import { ... } from 'iceberg-js' ✅ Full type support
    TypeScript CommonJS module: "CommonJS" import { ... } from 'iceberg-js' ✅ Full type support
    Bundlers Any Webpack, Vite, esbuild, Rollup, etc. ✅ Auto-detected
    Browsers ESM <script type="module"> ✅ Modern browsers
    Deno ESM import from npm: ✅ With npm specifier

    Package exports:

    • ESM: dist/index.mjs with dist/index.d.ts
    • CommonJS: dist/index.cjs with dist/index.d.cts
    • Proper exports field for Node.js 12+ module resolution

    All scenarios are tested in CI on Node.js 20 and 22.

    The library works in modern browsers that support native fetch:

    import { IcebergRestCatalog } from 'iceberg-js'

    const catalog = new IcebergRestCatalog({
    baseUrl: 'https://public-catalog.example.com/iceberg/v1',
    auth: { type: 'none' },
    })

    const namespaces = await catalog.listNamespaces()

    Node.js 20+ includes native fetch support. For older versions, provide a custom fetch implementation:

    import { IcebergRestCatalog } from 'iceberg-js'
    import fetch from 'node-fetch'

    const catalog = new IcebergRestCatalog({
    baseUrl: 'https://catalog.example.com/iceberg/v1',
    auth: { type: 'bearer', token: 'token' },
    fetch: fetch as any,
    })

    This is a catalog client only. The following are not supported:

    • Reading table data (scanning Parquet files)
    • Writing data to tables
    • Advanced table operations (commits, snapshots, time travel)
    • Views support
    • Multi-table transactions
    # Install dependencies
    pnpm install

    # Build the library
    pnpm run build

    # Run unit tests
    pnpm test

    # Run integration tests (requires Docker)
    pnpm test:integration

    # Run integration tests with cleanup (for CI)
    pnpm test:integration:ci

    # Run compatibility tests (all module systems)
    pnpm test:compatibility

    # Format code
    pnpm run format

    # Lint and test
    pnpm run check

    Integration tests run against a local Iceberg REST Catalog in Docker. See TESTING-DOCKER.md for details.

    # Start Docker services and run integration tests
    pnpm test:integration

    # Or manually
    docker compose up -d
    npx tsx test/integration/test-local-catalog.ts
    docker compose down -v

    The test:compatibility script verifies the package works correctly in all JavaScript/TypeScript environments:

    • Pure JavaScript ESM - Projects with "type": "module"
    • Pure JavaScript CommonJS - Traditional Node.js projects
    • TypeScript ESM - TypeScript with module: "ESNext"
    • TypeScript CommonJS - TypeScript with module: "CommonJS"

    These tests ensure proper module resolution, type definitions, and runtime behavior across all supported environments. See test/compatibility/README.md for more details.

    MIT

    This project uses release-please for automated releases. Here's how it works:

    1. Commit with conventional commits: Use Conventional Commits format for your commits:

      • feat: for new features (minor version bump)
      • fix: for bug fixes (patch version bump)
      • feat!: or BREAKING CHANGE: for breaking changes (major version bump)
      • chore:, docs:, test:, etc. for non-release commits
    2. Release PR is created automatically: When you push to main, release-please creates/updates a release PR with:

      • Version bump in package.json
      • Updated CHANGELOG.md
      • Release notes
    3. Merge the release PR: When you're ready to release, merge the PR. This will:

      • Create a GitHub release and git tag
      • Automatically publish to npm with provenance (using trusted publishing, no secrets needed)

    Example commits:

    git commit -m "feat: add support for view operations"
    git commit -m "fix: handle empty namespace list correctly"
    git commit -m "feat!: change auth config structure"

    Contributions are welcome! This library aims to be a minimal, generic client for the Iceberg REST Catalog API.