Search Shortcut cmd + k | ctrl + k

- Installation
- Documentation

- Getting Started
- Connect

- Overview
- Concurrency

- Data Import

- Overview
- Data Sources
- CSV Files

- Overview
- Auto Detection
- Reading Faulty CSV Files
- Tips

- JSON Files

- Overview
- Creating JSON
- Loading JSON
- Writing JSON
- JSON Type
- JSON Functions
- Format Settings
- Installing and Loading
- SQL to / from JSON
- Caveats

- Multiple Files

- Overview
- Combining Schemas

- Parquet Files

- Overview
- Metadata
- Encryption
- Tips

- Partitioning

- Hive Partitioning
- Partitioned Writes

- Appender
- INSERT Statements

- Client APIs

- Overview
- C

- Overview
- Startup
- Configuration
- Query
- Data Chunks
- Vectors
- Values
- Types
- Prepared Statements
- Appender
- Table Functions
- Replacement Scans
- API Reference

- C++
- CLI

- Overview
- Arguments
- Dot Commands
- Output Formats
- Editing
- Autocomplete
- Syntax Highlighting

- Dart
- Go
- Java
- Julia
- Node.js (Neo)

- Overview

- Node.js

- Overview
- API Reference

- Python

- Overview
- Data Ingestion
- Conversion between DuckDB and Python
- DB API
- Relational API
- Function API
- Types API
- Expression API
- Spark API
- API Reference
- Known Python Issues

- R
- Rust
- Swift
- Wasm

- Overview
- Instantiation
- Data Ingestion
- Query
- Extensions

- ADBC
- ODBC

- Overview
- Linux Setup
- Windows Setup
- macOS Setup
- Configuration

- SQL

- Introduction
- Statements

- Overview
- ANALYZE
- ALTER TABLE
- ALTER VIEW
- ATTACH and DETACH
- CALL
- CHECKPOINT
- COMMENT ON
- COPY
- CREATE INDEX
- CREATE MACRO
- CREATE SCHEMA
- CREATE SECRET
- CREATE SEQUENCE
- CREATE TABLE
- CREATE VIEW
- CREATE TYPE
- DELETE
- DESCRIBE
- DROP
- EXPORT and IMPORT DATABASE
- INSERT
- LOAD / INSTALL
- PIVOT
- Profiling
- SELECT
- SET / RESET
- SET VARIABLE
- SUMMARIZE
- Transaction Management
- UNPIVOT
- UPDATE
- USE
- VACUUM

- Query Syntax

- SELECT
- FROM and JOIN
- WHERE
- GROUP BY
- GROUPING SETS
- HAVING
- ORDER BY
- LIMIT and OFFSET
- SAMPLE
- Unnesting
- WITH
- WINDOW
- QUALIFY
- VALUES
- FILTER
- Set Operations
- Prepared Statements

- Data Types

- Overview
- Array
- Bitstring
- Blob
- Boolean
- Date
- Enum
- Interval
- List
- Literal Types
- Map
- NULL Values
- Numeric
- Struct
- Text
- Time
- Timestamp
- Time Zones
- Union
- Typecasting

- Expressions

- Overview
- CASE Statement
- Casting
- Collations
- Comparisons
- IN Operator
- Logical Operators
- Star Expression
- Subqueries

- Functions

- Overview
- Aggregate Functions
- Array Functions
- Bitstring Functions
- Blob Functions
- Date Format Functions
- Date Functions
- Date Part Functions
- Enum Functions
- Interval Functions
- Lambda Functions
- List Functions
- Map Functions
- Nested Functions
- Numeric Functions
- Pattern Matching
- Regular Expressions
- Struct Functions
- Text Functions
- Time Functions
- Timestamp Functions
- Timestamp with Time Zone Functions
- Union Functions
- Utility Functions
- Window Functions

- Constraints
- Indexes
- Meta Queries

- Information Schema
- Metadata Functions

- DuckDB's SQL Dialect

- Overview
- Indexing
- Friendly SQL
- Keywords and Identifiers
- Order Preservation
- PostgreSQL Compatibility

- Samples

- Configuration

- Overview
- Pragmas
- Secrets Manager

- Extensions

- Overview
- Core Extensions
- Community Extensions
- Working with Extensions
- Versioning of Extensions
- Arrow
- AutoComplete
- AWS
- Azure
- Delta
- Excel
- Full Text Search
- httpfs (HTTP and S3)

- Overview
- HTTP(S) Support
- Hugging Face
- S3 API Support
- Legacy Authentication Scheme for S3 API

- Iceberg
- ICU
- inet
- jemalloc
- MySQL
- PostgreSQL
- Spatial

- Overview
- Function Reference
- R-Tree Indexes
- GDAL Integration

- SQLite
- Substrait
- TPC-DS
- TPC-H
- VSS

- Guides

- Overview
- Data Viewers

- Tableau
- CLI Charting with YouPlot

- Database Integration

- Overview
- MySQL Import
- PostgreSQL Import
- SQLite Import

- File Formats

- Overview
- CSV Import
- CSV Export
- Directly Reading Files
- Excel Import
- Excel Export
- JSON Import
- JSON Export
- Parquet Import
- Parquet Export
- Querying Parquet Files

- Network and Cloud Storage

- Overview
- HTTP Parquet Import
- S3 Parquet Import
- S3 Parquet Export
- S3 Iceberg Import
- S3 Express One
- GCS Import
- Cloudflare R2 Import
- DuckDB over HTTPS / S3

- Meta Queries

- Describe Table
- EXPLAIN: Inspect Query Plans
- EXPLAIN ANALYZE: Profile Queries
- List Tables
- Summarize
- DuckDB Environment

- ODBC

- ODBC Guide

- Performance

- Overview
- Environment
- Import
- Schema
- Indexing
- Join Operations
- File Formats
- How to Tune Workloads
- My Workload Is Slow
- Benchmarks

- Python

- Installation
- Executing SQL
- Jupyter Notebooks
- SQL on Pandas
- Import from Pandas
- Export to Pandas
- Import from Numpy
- Export to Numpy
- SQL on Arrow
- Import from Arrow
- Export to Arrow
- Relational API on Pandas
- Multiple Python Threads
- Integration with Ibis
- Integration with Polars
- Using fsspec Filesystems

- SQL Editors

- DBeaver SQL IDE

- SQL Features

- AsOf Join
- Full-Text Search
- query and query_table Functions

- Snippets

- Create Synthetic Data
- Sharing Macros

- Glossary of Terms
- Browse Offline

- Operations Manual

- Overview
- Limits
- Non-Deterministic Behavior
- Embedding DuckDB
- DuckDB's Footprint

- Files Created by DuckDB
- Gitignore for DuckDB
- Reclaiming Space

- Securing DuckDB

- Overview
- Securing Extensions

- Development

- DuckDB Repositories
- Testing

- Overview
- sqllogictest Introduction
- Writing Tests
- Debugging
- Result Verification
- Persistent Testing
- Loops
- Multiple Connections
- Catch

- Profiling
- Release Calendar
- Building

- Overview
- Build Instructions
- Build Configuration
- Building Extensions
- Platforms
- Troubleshooting

- Benchmark Suite

- Internals

- Overview
- Storage Versions and Format
- Execution Format
- Pivot

- Sitemap
- Why DuckDB
- Media
- FAQ
- Code of Conduct
- Live Demo

Node.js API (Neo)

An API for using DuckDB in Node.js.

The primary package, @duckdb/duckdb-api, is a high-level API meant for applications. It depends on low-level bindings that adhere closely to DuckDB's C API, available separately as @duckdb/duckdb-bindings.

## Features

### Main differences from duckdb-node

- Native support for Promises; no need for separate duckdb-async wrapper.
- DuckDB-specific API; not based on the SQLite Node API.
- Lossless & efficent support for values of all DuckDB data types.
- Wraps released DuckDB binaries instead of rebuilding DuckDB.
- Built on DuckDB's C API; exposes more functionality.

### Roadmap

Some features are not yet complete:

- Appending and binding advanced data types. (Additional DuckDB C API support needed.)
- Writing to data chunk vectors. (Needs special handling in Node.)
- User-defined types & functions. (Support for this was added to the DuckDB C API in v1.1.0.)
- Profiling info (Added in v1.1.0)
- Table description (Added in v1.1.0)
- APIs for Arrow. (This part of the DuckDB C API is deprecated.)

### Supported Platforms

- Linux arm64 (experimental)
- Linux x64
- Mac OS X (Darwin) arm64 (Apple Silicon)
- Mac OS X (Darwin) x64 (Intel)
- Windows (Win32) x64

## Examples

### Get Basic Information

    import duckdb from '@duckdb/node-api';

    console.log(duckdb.version());

    console.log(duckdb.configurationOptionDescriptions());

### Create Instance

    import { DuckDBInstance } from '@duckdb/node-api';

Create with an in-memory database:

    const instance = await DuckDBInstance.create(':memory:');

Equivalent to the above:

    const instance = await DuckDBInstance.create();

Read from and write to a database file, which is created if needed:

    const instance = await DuckDBInstance.create('my_duckdb.db');

Set configuration options:

    const instance = await DuckDBInstance.create('my_duckdb.db', {
      threads: '4'
    });

### Connect

    const connection = await instance.connect();

### Run SQL

    const result = await connection.run('from test_all_types()');

### Parameterize SQL

    const prepared = await connection.prepare('select $1, $2');
    prepared.bindVarchar(1, 'duck');
    prepared.bindInteger(2, 42);
    const result = await prepared.run();

### Inspect Result

Get column names and types:

    const columnNames = result.columnNames();
    const columnTypes = result.columnTypes();

Fetch all chunks:

    const chunks = await result.fetchAllChunks();

Fetch one chunk at a time:

    const chunks = [];
    while (true) {
      const chunk = await result.fetchChunk();
      // Last chunk will have zero rows.
      if (!chunk || chunk.rowCount === 0) {
        break;
      }
      chunks.push(chunk);
    }

Read chunk data (column-major):

    // array of columns, each as an array of values
    const columns = chunk.getColumns();

Read chunk data (row-major):

    // array of rows, each as an array of values
    const rows = chunk.getRows();

Read chunk data (one value at a time):

    const columns = [];
    const columnCount = chunk.columnCount;
    for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
      const columnValues = [];
      const columnVector = chunk.getColumnVector(columnIndex);
      const itemCount = columnVector.itemCount;
      for (let itemIndex = 0; itemIndex < itemCount; itemIndex++) {
        const value = columnVector.getItem(itemIndex);
        columnValues.push(value);
      }
      columns.push(columnValues);
    }

### Result Reader

Run and read all data:

    const reader = await connection.runAndReadAll('from test_all_types()');
    const rows = reader.getRows();
    // OR: const columns = reader.getColumns();

Run and read up to (at least) some number of rows:

    const reader = await connection.runAndReadUtil('from range(5000)', 1000);
    const rows = reader.getRows();
    // rows.length === 2048. (Rows are read in chunks of 2048.)

Read rows incrementally:

    const reader = await connection.runAndRead('from range(5000)');
    reader.readUntil(2000);
    // reader.currentRowCount === 2048 (Rows are read in chunks of 2048.)
    // reader.done === false
    reader.readUntil(4000);
    // reader.currentRowCount === 4096
    // reader.done === false
    reader.readUntil(6000);
    // reader.currentRowCount === 5000
    // reader.done === true

### Inspect Data Types

    import { DuckDBTypeId } from '@duckdb/node-api';

    if (columnType.typeId === DuckDBTypeId.ARRAY) {
      const arrayValueType = columnType.valueType;
      const arrayLength = columnType.length;
    }

    if (columnType.typeId === DuckDBTypeId.DECIMAL) {
      const decimalWidth = columnType.width;
      const decimalScale = columnType.scale;
    }

    if (columnType.typeId === DuckDBTypeId.ENUM) {
      const enumValues = columnType.values;
    }

    if (columnType.typeId === DuckDBTypeId.LIST) {
      const listValueType = columnType.valueType;
    }

    if (columnType.typeId === DuckDBTypeId.MAP) {
      const mapKeyType = columnType.keyType;
      const mapValueType = columnType.valueType;
    }

    if (columnType.typeId === DuckDBTypeId.STRUCT) {
      const structEntryNames = columnType.names;
      const structEntryTypes = columnType.valueTypes;
    }

    if (columnType.typeId === DuckDBTypeId.UNION) {
      const unionMemberTags = columnType.memberTags;
      const unionMemberTypes = columnType.memberTypes;
    }

    // For the JSON type (https://duckdb.org/docs/data/json/json_type)
    if (columnType.alias === 'JSON') {
      const json = JSON.parse(columnValue);
    }

Every type implements toString. The result is both human-friendly and readable by DuckDB in an appropriate expression.

    const typeString = columnType.toString();

### Inspect Data Values

    import { DuckDBTypeId } from '@duckdb/node-api';

    if (columnType.typeId === DuckDBTypeId.ARRAY) {
      const arrayItems = columnValue.items; // array of values
      const arrayString = columnValue.toString();
    }

    if (columnType.typeId === DuckDBTypeId.BIT) {
      const bools = columnValue.toBools(); // array of booleans
      const bits = columnValue.toBits(); // arrary of 0s and 1s
      const bitString = columnValue.toString(); // string of '0's and '1's
    }

    if (columnType.typeId === DuckDBTypeId.BLOB) {
      const blobBytes = columnValue.bytes; // Uint8Array
      const blobString = columnValue.toString();
    }

    if (columnType.typeId === DuckDBTypeId.DATE) {
      const dateDays = columnValue.days;
      const dateString = columnValue.toString();
      const { year, month, day } = columnValue.toParts();
    }

    if (columnType.typeId === DuckDBTypeId.DECIMAL) {
      const decimalWidth = columnValue.width;
      const decimalScale = columnValue.scale;
      // Scaled-up value. Represented number is value/(10^scale).
      const decimalValue = columnValue.value; // bigint
      const decimalString = columnValue.toString();
      const decimalDouble = columnValue.toDouble();
    }

    if (columnType.typeId === DuckDBTypeId.INTERVAL) {
      const intervalMonths = columnValue.months;
      const intervalDays = columnValue.days;
      const intervalMicros = columnValue.micros; // bigint
      const intervalString = columnValue.toString();
    }

    if (columnType.typeId === DuckDBTypeId.LIST) {
      const listItems = columnValue.items; // array of values
      const listString = columnValue.toString();
    }

    if (columnType.typeId === DuckDBTypeId.MAP) {
      const mapEntries = columnValue.entries; // array of { key, value }
      const mapString = columnValue.toString();
    }

    if (columnType.typeId === DuckDBTypeId.STRUCT) {
      // { name1: value1, name2: value2, ... }
      const structEntries = columnValue.entries;
      const structString = columnValue.toString();
    }

    if (columnType.typeId === DuckDBTypeId.TIMESTAMP_MS) {
      const timestampMillis = columnValue.milliseconds; // bigint
      const timestampMillisString = columnValue.toString();
    }

    if (columnType.typeId === DuckDBTypeId.TIMESTAMP_NS) {
      const timestampNanos = columnValue.nanoseconds; // bigint
      const timestampNanosString = columnValue.toString();
    }

    if (columnType.typeId === DuckDBTypeId.TIMESTAMP_S) {
      const timestampSecs = columnValue.seconds; // bigint
      const timestampSecsString = columnValue.toString();
    }

    if (columnType.typeId === DuckDBTypeId.TIMESTAMP_TZ) {
      const timestampTZMicros = columnValue.micros; // bigint
      const timestampTZString = columnValue.toString();
      const {
        date: { year, month, day },
        time: { hour, min, sec, micros },
      } = columnValue.toParts();
    }

    if (columnType.typeId === DuckDBTypeId.TIMESTAMP) {
      const timestampMicros = columnValue.micros; // bigint
      const timestampString = columnValue.toString();
      const {
        date: { year, month, day },
        time: { hour, min, sec, micros },
      } = columnValue.toParts();
    }

    if (columnType.typeId === DuckDBTypeId.TIME_TZ) {
      const timeTZMicros = columnValue.micros; // bigint
      const timeTZOffset = columnValue.offset;
      const timeTZString = columnValue.toString();
      const {
        time: { hour, min, sec, micros },
        offset,
      } = columnValue.toParts();
    }

    if (columnType.typeId === DuckDBTypeId.TIME) {
      const timeMicros = columnValue.micros; // bigint
      const timeString = columnValue.toString();
      const { hour, min, sec, micros } = columnValue.toParts();
    }

    if (columnType.typeId === DuckDBTypeId.UNION) {
      const unionTag = columnValue.tag;
      const unionValue = columnValue.value;
      const unionValueString = columnValue.toString();
    }

    if (columnType.typeId === DuckDBTypeId.UUID) {
      const uuidHugeint = columnValue.hugeint; // bigint
      const uuidString = columnValue.toString();
    }

    // other possible values are: null, boolean, number, bigint, or string

### Append To Table

    await connection.run(
      `create or replace table target_table(i integer, v varchar)`
    );

    const appender = await connection.createAppender('main', 'target_table');

    appender.appendInteger(42);
    appender.appendVarchar('duck');
    appender.endRow();

    appender.appendInteger(123);
    appender.appendVarchar('mallard');
    appender.endRow();

    appender.flush();

    appender.appendInteger(17);
    appender.appendVarchar('goose');
    appender.endRow();

    appender.close(); // also flushes

### Extract Statements

    const extractedStatements = await connection.extractStatements(`
      create or replace table numbers as from range(?);
      from numbers where range < ?;
      drop table numbers;
    `);
    const parameterValues = [10, 7];
    const statementCount = extractedStatements.count;
    for (let stmtIndex = 0; stmtIndex < statementCount; stmtIndex++) {
      const prepared = await extractedStatements.prepare(stmtIndex);
      let parameterCount = prepared.parameterCount;
      for (let paramIndex = 1; paramIndex <= parameterCount; paramIndex++) {
        prepared.bindInteger(paramIndex, parameterValues.shift());
      }
      const result = await prepared.run();
      // ...
    }

### Control Evaluation of Tasks

    import { DuckDBPendingResultState } from '@duckdb/node-api';

    async function sleep(ms) {
      return new Promise((resolve) => {
        setTimeout(resolve, ms);
      });
    }

    const prepared = await connection.prepare('from range(10_000_000)');
    const pending = prepared.start();
    while (pending.runTask() !== DuckDBPendingResultState.RESULT_READY) {
      console.log('not ready');
      await sleep(1);
    }
    console.log('ready');
    const result = await pending.getResult();
    // ...

## Pages in This Section

##### About this page

- Report content issue
- Edit this page on GitHub