@ducklings/workers

中文文档
2.0.0发布于 2026/10/25MITTypeScriptESM + CJS构建来源已验证近 30 天下载 2.4万

DuckDB WASM for Cloudflare Workers with httpfs and Parquet

原始使用文档
文档目录19 节
  1. Installation
  2. Quick Start
  3. Singleton Initialization (Recommended)
  4. Vite Plugin
  5. Plugin Options
  6. Features
  7. Why a Separate Package?
  8. API
  9. Initialization
  10. Query Methods (Async)
  11. Prepared Statements
  12. Remote Files (httpfs)
  13. R2 Secrets
  14. Arrow IPC Endpoint
  15. Transactions
  16. Arrow Support
  17. Browser Usage
  18. Limitations
  19. License

@ducklings/workers

DuckDB WASM for Cloudflare Workers with only httpfs and parquet statically bundled. Async API with full TypeScript support.

Cloudflare allows 64 MiB uncompressed per Worker on both Free and Paid plans, with no compressed-size limit. Run wrangler deploy --dry-run --outdir bundled against your built Worker and check Total Upload. Choose a plan based on CPU and other runtime requirements.

Extension note: The published @ducklings/workers package does not bundle DuckDB's json extension to stay within Cloudflare's deployment size budget. JSON functions, the ::JSON type alias, and read_json() are therefore not available in the standard package. The published @ducklings/browser package omits the same extension for the same reason.

For the DuckLake-focused Workers build, use @ducklings/workers-ducklake.

For Avro or Iceberg queries, use @ducklings/workers-iceberg, which adds both extensions to httpfs and Parquet. Starting with 1.5.6, those applications must also switch their /wasm and /vite-plugin imports to that package.

Installation

npm install @ducklings/workers

Quick Start

import { init, DuckDB } from '@ducklings/workers';
import wasmModule from '@ducklings/workers/wasm';

export default {
  async fetch(request: Request): Promise<Response> {
    // Initialize with pre-compiled WASM module
    await init({ wasmModule });

    const db = new DuckDB();
    const conn = db.connect();

    // All queries are async
    const rows = await conn.query<{answer: number}>('SELECT 42 as answer');

    conn.close();
    db.close();

    return Response.json(rows);
  }
};

For production use, reuse the database and connection across requests to avoid re-initialization overhead:

import { init, DuckDB, type Connection } from '@ducklings/workers';
import wasmModule from '@ducklings/workers/wasm';

// Global state (reused across requests in the same Worker instance)
let db: DuckDB | null = null;
let conn: Connection | null = null;
let initialized = false;

async function ensureInitialized(): Promise<void> {
  if (initialized && db && conn) {
    return;
  }

  await init({ wasmModule });
  db = new DuckDB();
  conn = db.connect();
  initialized = true;
}

export default {
  async fetch(request: Request): Promise<Response> {
    await ensureInitialized();

    const rows = await conn!.query('SELECT 42 as answer');
    return Response.json(rows);
  }
};

Vite Plugin

For projects using Vite with @cloudflare/vite-plugin to build Cloudflare Workers, we provide a plugin that handles WASM file resolution and copying:

// vite.config.ts
import { defineConfig } from 'vite';
import { cloudflare } from '@cloudflare/vite-plugin';
import { ducklingsWorkerPlugin } from '@ducklings/workers/vite-plugin';

export default defineConfig({
  plugins: [
    ducklingsWorkerPlugin(),
    cloudflare(),
  ],
});

Plugin Options

ducklingsWorkerPlugin({
  // Name of the WASM file in the output directory (default: 'duckdb-workers.wasm')
  wasmFileName: 'duckdb-workers.wasm',
})

The plugin:

  • Resolves @ducklings/workers/wasm imports to a relative path for wrangler
  • Automatically copies the WASM file to the correct output directory (works with Cloudflare vite plugin's nested output structure)

Features

  • Async API - all query methods return Promises
  • Base WASM with Asyncify; omits Avro and Iceberg
  • Built-in Parquet and httpfs extensions
  • Full httpfs support via async fetch()
  • Arrow Table support via Flechette (query + insert)
  • Prepared statements with type-safe parameter binding
  • Transaction support

Why a Separate Package?

Cloudflare Workers doesn't support synchronous XMLHttpRequest (a browser-only API). This package uses Emscripten's Asyncify to enable async fetch() calls, making httpfs work properly for loading remote Parquet and CSV files, plus httpfs write paths.

The published browser and workers packages do not bundle the json extension. Remote Parquet and CSV access work in the standard packages. Remote JSON queries via read_json() do not.

PackageAPI StyleSize (gzipped)httpfs
@ducklings/browserAsync (Web Worker)~7.1 MiBXMLHttpRequest
@ducklings/workersAsync (Asyncify)~12.3 MiBfetch() via Asyncify

API

Initialization

import { init, version } from '@ducklings/workers';
import wasmModule from '@ducklings/workers/wasm';

// Initialize with pre-compiled WASM
await init({ wasmModule });

// Get DuckDB version
console.log(version()); // "v1.4.3"

Query Methods (Async)

const conn = db.connect();

// Returns array of objects
const rows = await conn.query<{id: number, name: string}>('SELECT * FROM users');

// Returns Arrow Table (Flechette)
const table = await conn.queryArrow('SELECT * FROM users');

// Execute without returning results
await conn.execute('INSERT INTO users VALUES (1, "Alice")');

// Insert Arrow IPC data into a table
const ipc = tableToIPC(arrowTable, { format: 'stream' });
await conn.insertArrowFromIPCStream('my_table', ipc);

Prepared Statements

const stmt = conn.prepare('SELECT * FROM users WHERE id = ? AND active = ?');
stmt.bindInt32(1, 42);
stmt.bindBoolean(2, true);
const results = await stmt.run();  // Note: async
stmt.close();

Remote Files (httpfs)

// Query remote Parquet file
const rows = await conn.query(`
  SELECT * FROM 'https://example.com/data.parquet'
  LIMIT 10
`);

// Query remote CSV
const csv = await conn.query(`
  SELECT * FROM read_csv('https://example.com/data.csv')
`);

read_json(), JSON functions such as json_extract(...), and the ::JSON type alias are not available in the standard workers package because DuckDB's json extension is not bundled. The standard browser package omits the same extension.

R2 Secrets

Access private S3-compatible storage using DuckDB secrets:


// Cloudflare R2
await conn.execute(`
  CREATE SECRET my_r2 (
    TYPE R2,
    KEY_ID 'your-r2-access-key-id',
    SECRET 'your-r2-secret-access-key',
    ACCOUNT_ID 'your-cloudflare-account-id'
  )
`);
const r2Data = await conn.query(`SELECT * FROM 'r2://bucket/file.parquet'`);

Supported secret types: S3, R2, GCS

For Cloudflare Workers, you can use Workers Secrets to securely store credentials. See the example worker for a complete implementation.

Arrow IPC Endpoint

Return query results as Arrow IPC stream for efficient data transfer:

import { init, DuckDB, tableToIPC } from '@ducklings/workers';
import wasmModule from '@ducklings/workers/wasm';

export default {
  async fetch(request: Request): Promise<Response> {
    await init({ wasmModule });

    const db = new DuckDB();
    const conn = db.connect();

    const table = await conn.queryArrow('SELECT * FROM range(1000) t(i)');
    const ipcBytes = tableToIPC(table, { format: 'stream' });

    conn.close();
    db.close();

    return new Response(ipcBytes, {
      headers: { 'Content-Type': 'application/vnd.apache.arrow.stream' },
    });
  }
};

Transactions

// Manual control
await conn.beginTransaction();
try {
  await conn.query('INSERT INTO accounts VALUES (1, 1000)');
  await conn.commit();
} catch (e) {
  await conn.rollback();
  throw e;
}

// Or use the wrapper (auto-rollback on error)
await conn.transaction(async () => {
  await conn.query('INSERT INTO orders VALUES (...)');
  return 'success';
});

Arrow Support

import { tableFromArrays, tableFromIPC, tableToIPC, utf8 } from '@ducklings/workers';

// Query as Arrow Table
const table = await conn.queryArrow('SELECT * FROM users');

// Build Arrow tables
const custom = tableFromArrays({
  id: [1, 2, 3],
  name: ['Alice', 'Bob', 'Charlie']
});

// Serialize/deserialize Arrow IPC
const bytes = tableToIPC(table, { format: 'stream' });
const restored = tableFromIPC(bytes);

// Insert Arrow IPC data directly into a table
const data = tableFromArrays(
  { id: [1, 2], label: ['x', 'y'] },
  { types: { label: utf8() } }  // Use plain utf8 (see note below)
);
const ipc = tableToIPC(data, { format: 'stream' });
await conn.insertArrowFromIPCStream('my_table', ipc);

Dictionary encoding: Flechette's tableFromArrays() defaults to dictionary(utf8()) for string columns. The Arrow IPC decoder used internally does not support dictionary-encoded streams. When building tables for insertArrowFromIPCStream(), explicitly set string columns to utf8():

import { utf8 } from '@ducklings/workers';
tableFromArrays({ col: ['a', 'b'] }, { types: { col: utf8() } });

Browser Usage

For browser environments, use @ducklings/browser instead, which runs queries in a Web Worker.

Limitations

  • No dynamic extension loading: Only statically compiled extensions are available. The default workers build includes Parquet and httpfs. Avro, Iceberg, DuckLake, Spatial, and JSON are not bundled. INSTALL/LOAD commands for other extensions will not work.

License

MIT

版本与发布记录

@ducklings/workers 共发布 18 个版本,首次发布于 2026/01/14;过去 12 个月发布了 18 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
2.0.0latest2026/10/2519 天前
1.5.62026/10/1529 天前
1.5.52026/08/192 个月前
1.5.3-dev.2dev2026/06/105 个月前
1.5.3-dev.12026/06/065 个月前
1.5.32026/06/065 个月前

关于 @ducklings/workers 的常见问题

@ducklings/workers 是什么?

DuckDB WASM for Cloudflare Workers with httpfs and Parquet

如何安装 @ducklings/workers?

在项目目录运行 npm install @ducklings/workers,或使用 pnpm add / yarn add / bun add。

@ducklings/workers 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@ducklings/workers 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@ducklings/workers 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@ducklings/workers 可以商用吗?

@ducklings/workers 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@ducklings/workers 的下载量有多少?

根据 npm 官方下载统计,@ducklings/workers 在 2026/10/13 – 2026/11/11 这 30 天内被下载 24,068 次,日均约 802 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@ducklings/workers 还在维护吗?

最新版本 2.0.0 发布于 2026/10/25(19 天前);至今共发布 18 个版本,其中过去 12 个月发布了 18 个;首次发布于 2026/01/14。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。