iceberg-js
A small, framework-agnostic JavaScript/TypeScript client for the Apache Iceberg REST Catalog.
Motivation
This library provides JavaScript and TypeScript developers with a straightforward way to interact with Apache Iceberg REST Catalogs. It's designed as a thin HTTP wrapper that mirrors the official REST API, making it easy to manage namespaces and tables from any JS/TS environment.
Goals
- REST API wrapper: Provide a 1:1 mapping to the Iceberg REST Catalog API
- Type safety: Full TypeScript support with strongly-typed request/response models
- Minimal footprint: No engine-specific logic, no heavy dependencies
- Stability: Production-ready for catalog management operations
- Vendor-agnostic: Works with any Iceberg REST Catalog implementation
Non-Goals
This library intentionally does not support:
- Data operations: Reading or writing table data (Parquet files, etc.)
- Query execution: Use dedicated query engines (Spark, Trino, DuckDB, etc.)
- Engine integration: No Spark, Flink, or other engine-specific code
- Advanced features: Branching, tagging, time travel queries beyond metadata
- Views or multi-table transactions
These boundaries keep the library focused and maintainable. For data operations, pair this library with a query engine that supports Iceberg.
Features
- 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
fetchAPI 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)
Documentation
📚 Full API documentation: supabase.github.io/iceberg-js
Installation
Quick Start
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', }, } )
API Reference
Constructor
new IcebergRestCatalog(options)
Creates a new catalog client instance.
Options:
baseUrl(string, required): Base URL of the REST catalogauth(AuthConfig, optional): Authentication configurationcatalogName(string, optional): Catalog name for multi-catalog servers. When specified, requests are sent to{baseUrl}/v1/{catalogName}/.... For example, withbaseUrl: 'https://host.com'andcatalogName: 'prod', requests go tohttps://host.com/v1/prod/namespacesfetch(typeof fetch, optional): Custom fetch implementationaccessDelegation(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 dataremote-signing: Server signs data access requests on behalf of the client
Namespace Operations
listNamespaces(parent?: NamespaceIdentifier): Promise<NamespaceIdentifier[]>
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'] }]
createNamespace(id: NamespaceIdentifier, metadata?: NamespaceMetadata): Promise<void>
Create a new namespace with optional properties.
await catalog.createNamespace({ namespace: ['analytics'] }, { properties: { owner: 'data-team' } })
dropNamespace(id: NamespaceIdentifier): Promise<void>
Drop a namespace. The namespace must be empty.
await catalog.dropNamespace({ namespace: ['analytics'] })
loadNamespaceMetadata(id: NamespaceIdentifier): Promise<NamespaceMetadata>
Load namespace metadata and properties.
const metadata = await catalog.loadNamespaceMetadata({ namespace: ['analytics'] }) // { properties: { owner: 'data-team', ... } }
Table Operations
listTables(namespace: NamespaceIdentifier): Promise<TableIdentifier[]>
List all tables in a namespace.
const tables = await catalog.listTables({ namespace: ['analytics'] }) // [{ namespace: ['analytics'], name: 'events' }]
createTable(namespace: NamespaceIdentifier, request: CreateTableRequest): Promise<TableMetadata>
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', }, ], }, } )
loadTable(id: TableIdentifier): Promise<TableMetadata>
Load table metadata.
const metadata = await catalog.loadTable({ namespace: ['analytics'], name: 'events', })
updateTable(id: TableIdentifier, request: UpdateTableRequest): Promise<CommitTableResponse>
Update table metadata (schema, partition spec, or properties).
const updated = await catalog.updateTable( { namespace: ['analytics'], name: 'events' }, { properties: { 'read.split.target-size': '134217728' }, } )
dropTable(id: TableIdentifier): Promise<void>
Drop a table from the catalog.
await catalog.dropTable({ namespace: ['analytics'], name: 'events' })
Error Handling
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' } }
TypeScript Types
The library exports all relevant types:
import type { NamespaceIdentifier, TableIdentifier, TableSchema, TableField, IcebergType, PartitionSpec, SortOrder, CreateTableRequest, TableMetadata, AuthConfig, AccessDelegation, } from 'iceberg-js'
Supported Iceberg Types
The following Iceberg primitive types are supported:
boolean,int,long,float,doublestring,uuid,binarydate,time,timestamp,timestamptzdecimal(precision, scale),fixed(length)
Compatibility
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.mjswithdist/index.d.ts - CommonJS:
dist/index.cjswithdist/index.d.cts - Proper
exportsfield for Node.js 12+ module resolution
All scenarios are tested in CI on Node.js 20 and 22.
Browser Usage
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 Usage
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, })
Development
# 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
Testing with Docker
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
Compatibility Testing
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.
License
Releases
This project uses release-please for automated releases. Here's how it works:
-
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!:orBREAKING CHANGE:for breaking changes (major version bump)chore:,docs:,test:, etc. for non-release commits
-
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
- Version bump in
-
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"
Contributing
Contributions are welcome! Please ensure your contributions align with the library's goals and non-goals. This library aims to remain a minimal, generic client for the Iceberg REST Catalog API.