PostgreSQL client library for Node.js with both pure JavaScript and optional native libpq bindings
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The pg type system provides comprehensive data conversion between JavaScript and PostgreSQL types, with support for custom type parsers, extensible type handling, and both text and binary format support.
The main types object provides global type parser configuration.
/**
* Global type parser registry from pg-types module
* Handles conversion between PostgreSQL and JavaScript types
*/
interface types {
/**
* Get the parser function for a PostgreSQL type
* @param oid - PostgreSQL type OID
* @param format - Format ('text' or 'binary', default 'text')
* @returns Parser function that converts string to JavaScript type
*/
getTypeParser(oid: number, format?: string): (value: string) => any;
/**
* Set a custom parser for a PostgreSQL type
* @param oid - PostgreSQL type OID
* @param format - Format ('text' or 'binary')
* @param parseFn - Parser function
*/
setTypeParser(oid: number, format: string, parseFn: (value: string) => any): void;
/**
* Set a custom parser for a PostgreSQL type (defaults to text format)
* @param oid - PostgreSQL type OID
* @param parseFn - Parser function
*/
setTypeParser(oid: number, parseFn: (value: string) => any): void;
}Usage Examples:
const { types } = require('pg');
// Get existing parser
const dateParser = types.getTypeParser(1082, 'text');
console.log(dateParser('2023-12-25')); // Parsed date
// Set custom date parser to return Date objects
types.setTypeParser(1082, (val) => new Date(val));
// Set custom JSON parser with error handling
types.setTypeParser(114, (val) => {
try {
return JSON.parse(val);
} catch (err) {
console.warn('Invalid JSON:', val);
return val;
}
});
// Binary format parser
types.setTypeParser(17, 'binary', (val) => {
return Buffer.from(val, 'hex');
});
// Numeric type with precision handling
types.setTypeParser(1700, (val) => {
return parseFloat(val);
});Per-client type parser overrides for customized type handling.
/**
* Client-specific type parser overrides
* Simple container for text and binary type parsers
*/
function TypeOverrides(userTypes) {
this.text = userTypes || {};
this.binary = userTypes || {};
}
TypeOverrides.prototype.setTypeParser = function(oid, format, parseFn) {
if (typeof format === 'function') {
parseFn = format;
format = 'text';
}
this[format][oid] = parseFn;
};
TypeOverrides.prototype.getTypeParser = function(oid, format) {
format = format || 'text';
return this[format][oid];
};Usage Examples:
const { TypeOverrides } = require('pg');
// Create client-specific type overrides
const customTypes = new TypeOverrides();
// Override date parsing for this client only
customTypes.setTypeParser(1082, (val) => {
const date = new Date(val);
return date.toISOString().split('T')[0]; // Return YYYY-MM-DD format
});
// Use with client
const client = new Client({
// ... connection config
types: customTypes
});
// Override JSON parsing with custom reviver
const apiTypes = new TypeOverrides();
apiTypes.setTypeParser(114, (val) => {
return JSON.parse(val, (key, value) => {
// Convert ISO date strings to Date objects
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
return new Date(value);
}
return value;
});
});
const apiClient = new Client({
// ... connection config
types: apiTypes
});Reference for common PostgreSQL type identifiers (use these numeric values when setting custom parsers).
// Numeric types
types.setTypeParser(21, parseFn); // smallint (INT2)
types.setTypeParser(23, parseFn); // integer (INT4)
types.setTypeParser(20, parseFn); // bigint (INT8)
types.setTypeParser(700, parseFn); // real (FLOAT4)
types.setTypeParser(701, parseFn); // double precision (FLOAT8)
types.setTypeParser(1700, parseFn); // numeric/decimal (NUMERIC)
// String types
types.setTypeParser(18, parseFn); // char (CHAR)
types.setTypeParser(1043, parseFn); // varchar (VARCHAR)
types.setTypeParser(25, parseFn); // text (TEXT)
types.setTypeParser(19, parseFn); // name (NAME)
// Date/time types
types.setTypeParser(1082, parseFn); // date (DATE)
types.setTypeParser(1083, parseFn); // time (TIME)
types.setTypeParser(1114, parseFn); // timestamp without timezone (TIMESTAMP)
types.setTypeParser(1184, parseFn); // timestamp with timezone (TIMESTAMPTZ)
types.setTypeParser(1186, parseFn); // interval (INTERVAL)
// Other common types
types.setTypeParser(17, parseFn); // bytea (BYTEA)
types.setTypeParser(16, parseFn); // boolean (BOOL)
types.setTypeParser(114, parseFn); // json (JSON)
types.setTypeParser(3802, parseFn); // jsonb (JSONB)
types.setTypeParser(2950, parseFn); // uuid (UUID)
// Array types
types.setTypeParser(1007, parseFn); // integer[] (INT4_ARRAY)
types.setTypeParser(1009, parseFn); // text[] (TEXT_ARRAY)Usage Examples:
// Custom UUID parser
types.setTypeParser(2950, (val) => val.toUpperCase());
// Custom JSONB parser
types.setTypeParser(3802, (val) => JSON.parse(val));
// Custom timestamp parser
types.setTypeParser(1184, (val) => new Date(val));
// Array type parsing
types.setTypeParser(1007, (val) => {
// Parse PostgreSQL array format: {1,2,3}
return val.slice(1, -1).split(',').map(Number);
});
types.setTypeParser(1009, (val) => {
// Handle quoted strings in arrays: {"hello","world"}
return val.slice(1, -1).split(',').map(s => s.replace(/^"|"$/g, ''));
});Standard type conversions provided by pg-types.
/**
* Default type conversions applied by pg
* These can be overridden using setTypeParser
*/
interface DefaultTypeConversions {
// Numeric conversions
smallint: number; // INT2 -> number
integer: number; // INT4 -> number
bigint: string; // INT8 -> string (to avoid precision loss)
real: number; // FLOAT4 -> number
double: number; // FLOAT8 -> number
numeric: string; // NUMERIC -> string (preserves precision)
// String conversions
char: string; // CHAR -> string
varchar: string; // VARCHAR -> string
text: string; // TEXT -> string
// Date/time conversions
date: string; // DATE -> 'YYYY-MM-DD'
time: string; // TIME -> 'HH:MM:SS'
timestamp: string; // TIMESTAMP -> 'YYYY-MM-DD HH:MM:SS'
timestamptz: string; // TIMESTAMPTZ -> ISO string
interval: string; // INTERVAL -> PostgreSQL interval format
// Other conversions
boolean: boolean; // BOOL -> boolean
bytea: Buffer; // BYTEA -> Buffer
json: string; // JSON -> string (not parsed)
jsonb: string; // JSONB -> string (not parsed)
uuid: string; // UUID -> string
}Common patterns for implementing custom type parsers.
/**
* Example custom parsers for common use cases
*/Usage Examples:
// Parse bigint as number (with precision loss warning)
types.setTypeParser(20, (val) => {
const num = parseInt(val, 10);
if (num > Number.MAX_SAFE_INTEGER) {
console.warn('Bigint value exceeds JavaScript safe integer range');
}
return num;
});
// Parse numeric/decimal with precision
types.setTypeParser(1700, (val) => {
// Use decimal.js for precision
const Decimal = require('decimal.js');
return new Decimal(val);
});
// Parse timestamps as Date objects with timezone handling
types.setTypeParser(1114, (val) => {
// Timestamp without timezone - assume UTC
return new Date(val + 'Z');
});
types.setTypeParser(1184, (val) => {
// Timestamp with timezone
return new Date(val);
});
// Parse intervals as duration objects
types.setTypeParser(1186, (val) => {
// Parse PostgreSQL interval format
const match = val.match(/(\d+):(\d+):(\d+)/);
if (match) {
return {
hours: parseInt(match[1], 10),
minutes: parseInt(match[2], 10),
seconds: parseInt(match[3], 10)
};
}
return val;
});
// Parse JSON/JSONB automatically
types.setTypeParser(114, JSON.parse); // json
types.setTypeParser(3802, JSON.parse); // jsonb
// Parse arrays with proper handling
types.setTypeParser(1007, (val) => {
// integer[] parser
if (val === '{}') return [];
return val.slice(1, -1).split(',').map(v => v === 'NULL' ? null : parseInt(v, 10));
});
// Parse UUIDs with validation
types.setTypeParser(2950, (val) => {
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!uuidRegex.test(val)) {
throw new Error(`Invalid UUID format: ${val}`);
}
return val.toLowerCase();
});
// Parse network addresses
types.setTypeParser(869, (val) => { // inet
return {
address: val.split('/')[0],
netmask: val.split('/')[1] ? parseInt(val.split('/')[1], 10) : 32
};
});Utilities for debugging and inspecting type parsing.
/**
* Debug utilities for type system
*/Usage Examples:
// Log all registered parsers
function debugTypeParsers() {
const { types } = require('pg');
// Common type OIDs to check
const typeOIDs = [16, 17, 20, 21, 23, 25, 114, 1082, 1114, 1184, 1700, 2950, 3802];
typeOIDs.forEach(oid => {
const textParser = types.getTypeParser(oid, 'text');
const binaryParser = types.getTypeParser(oid, 'binary');
console.log(`OID ${oid}: text=${textParser.name || 'anonymous'}, binary=${binaryParser.name || 'anonymous'}`);
});
}
// Test type parsing
function testTypeParser(oid, testValue) {
const { types } = require('pg');
const parser = types.getTypeParser(oid, 'text');
try {
const result = parser(testValue);
console.log(`OID ${oid}: "${testValue}" -> ${typeof result} ${JSON.stringify(result)}`);
return result;
} catch (err) {
console.error(`OID ${oid}: Error parsing "${testValue}":`, err.message);
return null;
}
}
// Usage
debugTypeParsers();
testTypeParser(1082, '2023-12-25');
testTypeParser(114, '{"key": "value"}');
testTypeParser(20, '9223372036854775807');/**
* Configuration interfaces for type system
*/
interface TypeSystemConfig {
/** Global types object */
types: types;
/** Client-specific overrides */
TypeOverrides: typeof TypeOverrides;
/** Common type OID constants */
oids: { [typeName: string]: number };
}
/**
* Type parser function signature
*/
type TypeParser<T = any> = (value: string) => T;
/**
* Type override map
*/
interface TypeOverrideMap {
[oid: number]: TypeParser;
}Install with Tessl CLI
npx tessl i tessl/npm-pg