Neon Bindings Examples

repository·main·Indexed 43 days ago

https://github.com/neon-bindings/examples

A collection of practical examples and common patterns for building high-performance bindings between Rust and JavaScript using Neon. Includes implementations for async SQLite operations, CPU count retrieval, gzip transform streams via CompressStream, basic hello-world bindings, and asynchronous task offloading using the tokio thread pool.

Tokens
2.2K
Snippets
10
Records
15
Agent score
56%

What's inside Neon

  1. Use the gzip-stream example to create async transform streams

    main

    The gzip-stream example demonstrates how to build a Node.js "through" stream using Neon. This implementation creates a Transform stream that is both readable and writeable, offloading CPU-intensive gzip compression to the Node worker thread pool. This pattern allows you to integrate high-performance Rust-based processing into standard Node.js stream pipelines while maintaining backpressure and asynchronous behavior.

    // The example uses a glue function to wrap the Neon CompressStream into a Node.js Transform stream
    const stream = compress();
    // 'stream' can now be used in standard Node.js piping: 
    // sourceStream.pipe(stream).pipe(destinationStream);
  2. Available examples in neon-examples

    main

    The repository contains several specialized examples:

    • async-sqlite: Provides an async interface to a SQLite database.
    • cpu-count: Returns the number of CPUs available on the system.
    • gzip-stream: Demonstrates how to asynchronously compress a stream of data.
    • hello-world: A basic example that returns a JS String with a greeting message.
    • tokio-fetch: Demonstrates asynchronously fetching a Node.js release date.
  3. How Async SQLite handles concurrency between Rust and JavaScript

    main

    Because SQLite is a synchronous interface that blocks the executing thread, this implementation uses a multi-threaded pattern to maintain JavaScript responsiveness:

    1. Dedicated Database Thread: A single Rust thread is spawned specifically for performing database operations. This avoids the need for a connection pool since SQLite is naturally single-threaded.
    2. Communication via MPSC: A multi-producer, single-consumer (mpsc) channel is used for one-way communication from the JavaScript main thread to the database thread. The sender side of this channel is stored within a JavaScript class (using #[neon::export(class)]).
    3. Event Callbacks: To return data back to JavaScript, the database thread uses a neon::event::Channel. This allows the Rust thread to send events back to the JavaScript main thread to resolve promises or trigger callbacks once the query is complete.
  4. Implement a Node.js Transform stream with CompressStream

    main

    To use the CompressStream Neon module within a standard Node.js Transform stream, you must implement the transform and flush methods. The glue code adapts the CompressStream Promise-based API to the callback-based requirements of Node.js streams.

    function compress() {
        const stream = new CompressStream();
    
        return new Transform({
            transform(chunk, encoding, callback) {
                stream
                    .compress(encoding, chunk)
                    .then(data => callback(null, data))
                    .catch(callback);
            },
    
            flush(callback) {
                stream
                    .finish()
                    .then(data => callback(null, data))
                    .catch(callback);
            }
        });
    }
  5. Setup the neon-examples repository

    main

    The examples repository uses the npm 7 [workspaces] feature. To set up the development environment, ensure you are using npm 7 or higher, clone the repository, and run npm install from the root directory.

    # Ensure npm 7+ is installed
    npm --version
    
    git clone https://github.com/neon-bindings/examples.git
    cd examples
    npm install
  6. Use the cpu-count example to get the number of CPUs

    main

    The cpu-count example provides a single function, getNumCpus, which returns the number of CPUs available on the current platform. This is useful for determining concurrency limits or optimizing workload distribution in Node.js applications.

    const { getNumCpus } = require(".");
    
    console.log(getNumCpus());
  7. Use the Async SQLite JavaScript API

    main

    The async-sqlite example provides a Database class that allows you to perform asynchronous SQLite operations from JavaScript. This pattern is useful for preventing the JavaScript main thread from blocking during synchronous SQLite queries by offloading work to a dedicated Rust thread.

    To use it, instantiate the Database class and use await on methods like insert and byId to handle the asynchronous results returned from the database thread.

    const { Database } = require(".");
    
    (async () => {
        const db = new Database();
    
        const id = await db.insert("Marty McFly");
        const name = await db.byId(id);
    
        console.log(name);
    })();
  8. Use `nodeReleaseDate` to fetch a specific Node.js release date

    main

    The nodeReleaseDate function asynchronously fetches the release date for a specified Node.js version from nodejs.org. In the underlying Rust implementation, this is an async fn that utilizes TryFromJs and TryIntoJs for ergonomic data conversion between JavaScript and Rust.

    // Returns a Promise that resolves to the release date string
    const date = await nodeReleaseDate('v18.0.0');
  9. CompressStream API Reference

    main

    The CompressStream class is the core Neon module for gzip compression. It provides methods to process data in chunks and finalize the compression stream.

    // Create a new instance
    const stream = new CompressStream(level);
    
    // Compress a chunk
    const buffer = await stream.compress(chunk, encoding);
    
    // Finalize compression
    const finalBuffer = await stream.finish();