@neondatabase/serverless

repository·main·Indexed 19 days ago

https://github.com/neondatabase/serverless

A high-performance PostgreSQL driver for JavaScript and TypeScript optimized for serverless and edge environments. It provides a low-latency HTTPS-based query function via neon(), non-interactive transactions via sql.transaction(), and WebSocket-based session support through Pool and Client constructors. It serves as a drop-in replacement for node-postgres (pg) in serverless contexts.

Tokens
20.6K
Snippets
61
Records
78
Agent score
68%

What's inside @neondatabase/serverless

  1. Use Pool and Client for sessions and transactions

    main

    When you require session support, interactive transactions, or compatibility with query builders like Kysely or Zapatos, use the Pool or Client constructors instead of the neon() function. These use WebSockets for communication.

    Critical Usage Rules for Serverless/Edge

    In environments like Vercel Edge Functions or Cloudflare Workers, WebSocket connections cannot outlive a single request.

    1. Scope: You must create, use, and close Pool or Client objects within a single request handler.
    2. Avoid Global Scope: Do not create these objects outside the request handler.
    3. Cleanup: Always close the connection (e.g., using pool.end() or client.end()) to avoid exhausting available connections. In Vercel Edge Functions, use ctx.waitUntil(pool.end()) to ensure the connection closes without delaying the response.

    Node.js WebSocket Configuration

    In Node.js v21 and earlier, you must manually provide a WebSocket constructor (e.g., from the ws package) via neonConfig.webSocketConstructor.

    import { Pool, neonConfig } from '@neondatabase/serverless';
    import ws from 'ws';
    
    // Required for Node v21 and below
    neonConfig.webSocketConstructor = ws;
    
    const pool = new Pool({ connectionString: process.env.DATABASE_URL });
  2. Deploy wsproxy with Nginx TLS proxy on Ubuntu

    main

    This guide provides the sequence of commands to deploy wsproxy behind Nginx for TLS on an Ubuntu 22.04 host.

    Prerequisites:

    • Port 443 must be accessible (check firewall settings).
    • Host must be running Ubuntu 22.04 (older versions may have incompatible Go versions).

    Workflow:

    1. Upgrade Ubuntu to 22.04.
    2. Install and configure PostgreSQL with a password-authenticated user.
    3. Build and install wsproxy from source.
    4. Configure wsproxy as a systemd service.
    5. Install Nginx and Certbot to handle TLS termination.
    6. Configure Nginx to proxy WebSocket traffic to wsproxy.
    # 1. Upgrade Ubuntu
    sudo su
    apt update -y && apt upgrade -y && apt dist-upgrade -y
    apt autoremove -y && apt autoclean -y
    apt install -y update-manager-core
    do-release-upgrade
    
    # 2. Setup Postgres
    export HOSTDOMAIN=ws.example.com
    apt install -y postgresql
    echo 'create database wstest; create user wsclear; grant all privileges on database wstest to wsclear;' | sudo -u postgres psql
    # Note: Run \password wsclear manually in psql to set password
    perl -pi -e 's/^# IPv4 local connections:\n/# IPv4 local connections:\nhost all wsclear 127.0.0.1\/32 password\n/' /etc/postgresql/14/main/pg_hba.conf
    service postgresql restart
    
    # 3. Install wsproxy
    adduser wsproxy --disabled-login
    sudo su wsproxy
    cd
    git clone https://github.com/neondatabase/wsproxy.git
    cd wsproxy
    go build
    exit
    
    # 4. Configure systemd service
    echo "
    [Unit]
    Description=wsproxy
    
    [Service]
    Type=simple
    Restart=always
    RestartSec=5s
    User=wsproxy
    Environment=LISTEN_PORT=:6543 ALLOW_ADDR_REGEX='^${HOSTDOMAIN}:5432\$"
    ExecStart=/home/wsproxy/wsproxy/wsproxy
    
    [Install]
    WantedBy=multi-user.target
    " > /lib/systemd/system/wsproxy.service
    
    systemctl enable wsproxy
    service wsproxy start
    
    # 5. Install Nginx and Certbot
    apt install -y golang nginx certbot python3-certbot-nginx
    echo "127.0.0.1 ${HOSTDOMAIN}" >> /etc/hosts
    
    # 6. Configure Nginx site
    echo "
    server {
      listen 80;
      listen [::]:80;
      server_name ${HOSTDOMAIN};
      location / {
        proxy_pass http://127.0.0.1:6543/;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection Upgrade;
        proxy_set_header Host \$host;
      }
    }
    " > /etc/nginx/sites-available/wsproxy
    
    ln -s /etc/nginx/sites-available/wsproxy /etc/nginx/sites-enabled/wsproxy
    certbot --nginx -d ${HOSTDOMAIN}
    
    # Final Nginx config is managed by Certbot; restart service
    service nginx restart
  3. Run tests for @neondatabase/serverless

    main

    To run the test suite locally, ensure you have Node LTS, npm, Bun, and Deno installed. You must also configure the test environment variables by creating a .env.test file from the provided template.

    1. Copy .env.template to .env.test and fill in the required values.
    2. Install dependencies using npm install.
    3. Execute the tests using npm test.
    cp .env.template .env.test
    # Fill in the blanks in .env.test
    npm install
    npm test
  4. Deploy a WebSocket proxy for your own Postgres instance

    main

    If you are not using a Neon database, you can run your own WebSocket proxy to allow @neondatabase/serverless to connect to your own Postgres instances. This requires setting up a proxy (such as wsproxy) and securing it using one of two methods:

    1. Nginx as a TLS Proxy: Place Nginx in front of wsproxy to handle TLS. In this setup, onward traffic from the proxy to Postgres is not secured by Nginx, so Postgres should reside on the same machine or be reached via a private network.
    2. Experimental Pure-JS Encryption (subtls): Use the subtls library for end-to-end encryption. This does not require Nginx. To use this, set neonConfig.useSecureWebSocket and neonConfig.forceDisablePgSSL to false, and append ?sslmode=verify-full (or similar) to your connection string. Note: subtls is experimental and not recommended for production.

    After setting up the proxy, you must configure the @neondatabase/serverless package with the wsProxy option (and subtls and rootCerts if using the experimental method).

  5. Install @neondatabase/serverless from a specific branch or commit

    main

    If you need to use a specific version of the package that is not yet published to npm (such as a specific branch or a commit hash from the GitHub repository), you can install it directly using the following npm syntax.

    npm install @neondatabase/serverless@github:neondatabase/serverless#BRANCH_OR_COMMIT
  6. Set `webSocketConstructor` for Node.js environments

    main

    If you are using the driver in an environment where the WebSocket global is not defined (such as Node.js) and you require transaction or session support, you must provide a WebSocket implementation via webSocketConstructor in the global neonConfig.

    import { neonConfig } from '@neondatabase/serverless';
    import ws from 'ws';
    
    neonConfig.webSocketConstructor = ws;
  7. Configure global or instance-specific `neonConfig`

    main

    You can configure @neondatabase/serverless in two ways:

    1. Globally: Import neonConfig from the package and set properties on it. This affects all subsequent client instances.
    2. Per-instance: Set options on an individual Client instance using its neonConfig property. This overrides the global defaults for that specific client.

    Note that some configuration options (like webSocketConstructor, poolQueryViaFetch, fetchEndpoint, and fetchFunction) can only be set globally.

    import { Client, neonConfig } from '@neondatabase/serverless';
    import ws from 'ws';
    
    // Set default option for all clients
    neonConfig.webSocketConstructor = ws;
    
    // Override the default option on an individual client
    const client = new Client(process.env.DATABASE_URL);
    client.neonConfig.webSocketConstructor = ws;
  8. Install @neondatabase/serverless

    main

    Install the Neon PostgreSQL driver for JavaScript and TypeScript using your preferred package manager.

    npm:

    npm install @neondatabase/serverless

    JSR:

    bunx jsr add @neon/serverless

    Note for existing pg dependencies: If you need to use this driver as a drop-in replacement for node-postgres (the pg package) in a project where other dependencies declare pg, use an alias and override in your package.json:

      "dependencies": {
        "pg": "npm:@neondatabase/serverless@^1.0.0"
      },
      "overrides": {
        "pg": "npm:@neondatabase/serverless@^1.0.0"
      }
  9. How sql.transaction() works

    main

    The sql.transaction() method implements batching for serverless environments. Instead of sending multiple round-trips, it collects an array of queries and sends them to the Neon server in a single request. This ensures that the entire batch is executed within a single PostgreSQL transaction block on the server side, reducing latency and ensuring atomicity.

    // The mental model: 
    // 1. Define queries using the sql template
    // 2. Wrap them in an array
    // 3. Pass that array to sql.transaction()
    // 4. Neon executes them as a single batch/transaction
    await sql.transaction([sql`UPDATE x SET y=1`, sql`UPDATE x SET y=2"]);
  10. Pass custom fetch options via `fetchOptions`

    main

    The fetchOptions option allows you to pass a Record<string, any> that is merged with the underlying fetch call options. This can be applied to the neon() constructor, the transaction() function, or individual .query() calls.

    Common use cases include:

    • Setting request priority (e.g., 'high').
    • Implementing timeouts using an AbortController signal.
    import { neon } from '@neondatabase/serverless';
    
    // Set high priority for all queries
    const sql = neon(process.env.DATABASE_URL, {
      fetchOptions: { priority: 'high' },
    });
    
    // Implement a timeout for a specific query
    const abortController = new AbortController();
    const timeout = setTimeout(() => abortController.abort('timed out'), 10000);
    
    try {
      const rows = await sql.query('SELECT * FROM posts WHERE id = $1', [postId], {
        fetchOptions: { signal: abortController.signal },
      });
    } finally {
      clearTimeout(timeout);
    }
  11. Configure query return formats with `arrayMode` and `fullResults`

    main

    You can customize how query results are returned by setting arrayMode or fullResults in the neon() constructor or the .query() options.

    • arrayMode: boolean: When true, rows are returned as an array of arrays (e.g., [[val1, val2]]) instead of an array of objects. This is useful for performance or specific data shapes.
    • fullResults: boolean: When true, the return value includes metadata similar to node-postgres (e.g., fields, rowCount, command), with the actual data located in the rows property.
    import { neon } from '@neondatabase/serverless';
    
    // Array mode: returns [[val1, val2]]
    const sqlArray = neon(process.env.DATABASE_URL, { arrayMode: true });
    
    // Full results: returns { rows: [...], fields: [...], rowCount: 1, ... }
    const sqlFull = neon(process.env.DATABASE_URL, { fullResults: true });
    
    // Using options directly in a query call
    const results = await sql.query('SELECT * FROM posts WHERE id = $1', [postId], {
      arrayMode: true,
      fullResults: true
    });
  12. Authenticate requests using `authToken`

    main

    The authToken option sets the Authorization header for the underlying fetch requests. This allows you to pass credentials (like a JWT) to authenticate database requests against third-party providers.

    import { neon } from '@neondatabase/serverless';
    
    // authToken can be a string or a function returning a Promise<string>
    const sql = neon(process.env.DATABASE_URL, { 
      authToken: async () => await getAuthToken() 
    });
    
    const posts = await sql`SELECT * FROM posts`;