Skip to content

Conversation

@Jobians
Copy link

@Jobians Jobians commented Sep 19, 2025

This PR introduces three changes to make the pg package more compatible with Deno while
keeping full Node.js functionality:

  1. Default user value

    • Replace user: process.platform === 'win32' ? process.env.USERNAME : process.env.USER
      with user: 'postgres' in defaults.
    • Avoids requiring environment variables in Deno.
  2. Config-first parameter resolution

    • Update val() in connection-parameters.js to return config[key] first, before
      checking environment variables.
    • Prevents Deno errors when --allow-env is not granted.
  3. Safe NODE_PG_FORCE_NATIVE check

    • Wrap the NODE_PG_FORCE_NATIVE check in a try/catch.
    • Ensures process.env access in Deno doesn’t throw, while preserving Node.js behavior.

These changes maintain Node.js compatibility, preserve the lazy-loading of the native module,
and allow using the package in Deno without requiring --allow-env.

…rors

Currently, the pg package defaults the database user to process.env.USER (or USERNAME on Windows).  
This causes errors when running in Deno without --allow-env, as environment access is restricted.

This PR changes the default user to 'postgres', so:

- Node.js behavior is unchanged when a user is explicitly provided in config.
- Deno users no longer hit NotCapable errors for environment access.
- Avoids relying on process.env for default values.

Example:

Before:
user: process.platform === 'win32' ? process.env.USERNAME : process.env.USER,

After:
user: 'postgres', // default user, avoids using process.env
…meters

Previously, ConnectionParameters would first check process.env for connection settings before using the provided config object. This could cause errors in environments like Deno where environment access is restricted.

This change updates the val function to:

1. Use the value from config if it is defined.


2. Fall back to environment variables only if config does not provide a value.


3. Use default values if neither config nor environment variables are set.



This ensures that explicitly provided configuration values are always respected, avoiding unnecessary errors in restricted environments.
Replace the `typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined'` check with a try/catch
to safely handle environments like Deno without `--allow-env`.

- Keeps Node.js behavior unchanged.
- Prevents errors when accessing process.env in Deno.
- Preserves lazy loading of the native module.
Wrap the default database user assignment in a try/catch to prevent errors when environment access is restricted (e.g., in Deno).

- Uses process.env.USER / USERNAME on Node if available
- Falls back to 'postgres' when env access fails or is unavailable
- Maintains code structure and comments
- Ensures Node tests continue to pass while preventing Deno runtime errors
@Jobians Jobians requested a review from charmander September 20, 2025 13:17
@Jobians
Copy link
Author

Jobians commented Sep 25, 2025

@charmander

@Jobians
Copy link
Author

Jobians commented Oct 9, 2025

??? I will close this is you don't respond again

Copy link

@NickCrews NickCrews left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the biggest downside of the old pattern, and this suggested implementation, is that they bind too early. We don't need to, and therefore shouldn't decide which backend to use (either the js backend or the native backend) until the user actually tries to create a Client. If we defer this decision until then, then we open up the possibility for the user to request a different backend using a different API beside env vars (because if you decide at import time, this package has no APIs available yet for the user to inform us what they want).

So what about something like the following, which then provides an actual, non-env way to configure which backend the user wants? I don't love the use of a global variable to define the the behavior, but it is the least api-disruptive

const PG = function (clientConstructor) {
  this.defaults = defaults
  this.Client = clientConstructor
  this.Query = this.Client.Query
  this.Pool = poolFactory(this.Client)
  this._pools = []
  this.Connection = Connection
  this.types = require('pg-types')
  this.DatabaseError = DatabaseError
  this.TypeOverrides = TypeOverrides
  this.escapeIdentifier = escapeIdentifier
  this.escapeLiteral = escapeLiteral
  this.Result = Result
  this.utils = utils
  // A new property/config item we add? Not needed if you don't want to
  // increase your API surface area, or maybe requires some more thought.
  this.forceNative = false
}

function makeClient(config) {
    // start with the current config setting
    let useNative = module.exports.forceNative
    try {
        useNative = typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined'
    } catch {
        // ignore, eg in Deno sandbox without --allow-env
    }
    const actualClient = useNative ? require('./native') : Client
    return actualClient(config)
}

module.exports = new PG(makeClient)

// lazy require native module...the native module may not have installed
  Object.defineProperty(module.exports, 'native', {
...

eg user does

import {Client, forceNative} from 'pg'

forceNative = true
const nativeClient = Client(...)
forceNative = false
const jsClient = Client(...)

// ignore, e.g., Deno without --allow-env
}

if (forceNative) {
Copy link

@NickCrews NickCrews Oct 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I am reading this right, if you are in a deno environment with no --allow-env, then the above will always error, and forceNative will always remain in its default of false, and so it will be impossible to use the native driver. I'm not sure how much of a dealbreaker this actually is (eg does the native driver actually run in deno???).

Copy link
Collaborator

@charmander charmander Oct 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the environment variable was always a mistake (same with the property), and breaking it in a way that’s backwards-compatible is fine. If a future API of the pg package even includes pg-native at all, I suggest it should be imported from pg/native and that there should be only the one way to do it. (I also recommend never using pg-native, since it has extra bugs and less maintenance. The inherent performance difference is minimal, though both pg and pg-native are both missing key optimizations right now.)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I also agree that the best API would be to import from pg/native. Then we can skip any new APIs here to select the backend. How about this then?

const PG = ... // same as before

function makeClient(config) {
    let useNative = false;
    try {
        useNative = typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined'
    } catch {
        // ignore, eg in Deno sandbox without --allow-env
    }
    const actualClient = useNative ? require('./native') : Client
    return actualClient(config)
}

module.exports = new PG(makeClient)

// lazy require native module...the native module may not have installed
  Object.defineProperty(module.exports, 'native', {
...

Is there anything else you need to merge this PR? With that change I'm happy. Honestly, the current suggested implementation is similar enough, I would be fine merging that as well.

@@ -1,11 +1,18 @@
'use strict'

let user

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe say explicitly let user = undefined? I know they behave the same, but semantically this bare let user (incorrectly) implies to me that we expect to always set it to a value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants