Back to Documentation

JavaScript/TypeScript SDK

Official Node.js and browser client for entropyDB

Installation

# npm
npm install @entropydb/client

# yarn
yarn add @entropydb/client

# pnpm
pnpm add @entropydb/client

Quick Start

import { EntropyClient } from '@entropydb/client';

// Connect to entropyDB
const client = new EntropyClient({
  host: 'localhost',
  port: 5432,
  database: 'mydb',
  user: 'admin',
  password: 'password',
});

await client.connect();

// Execute a query
const result = await client.query(
  'SELECT * FROM users WHERE id = $1',
  [123]
);

console.log(result.rows);

// Close connection
await client.close();

Connection Management

Connection Pooling

import { Pool } from '@entropydb/client';

const pool = new Pool({
  host: 'localhost',
  port: 5432,
  database: 'mydb',
  user: 'admin',
  password: 'password',
  max: 20,              // Maximum pool size
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

// Automatic connection management
const result = await pool.query('SELECT NOW()');

// Get a client from pool for transactions
const client = await pool.connect();
try {
  await client.query('BEGIN');
  await client.query('INSERT INTO users...');
  await client.query('COMMIT');
} catch (e) {
  await client.query('ROLLBACK');
  throw e;
} finally {
  client.release();
}

// Shutdown pool
await pool.end();

Environment Variables

// .env file
ENTROPYDB_HOST=localhost
ENTROPYDB_PORT=5432
ENTROPYDB_DATABASE=mydb
ENTROPYDB_USER=admin
ENTROPYDB_PASSWORD=secret

// Use in code
import { EntropyClient } from '@entropydb/client';

const client = new EntropyClient({
  connectionString: process.env.DATABASE_URL,
  // or individual vars
  host: process.env.ENTROPYDB_HOST,
  port: parseInt(process.env.ENTROPYDB_PORT),
  // ...
});

Query Execution

Parameterized Queries

// Always use parameterized queries to prevent SQL injection
const result = await client.query(
  'SELECT * FROM users WHERE email = $1 AND active = $2',
  ['user@example.com', true]
);

// Access results
console.log(result.rows);        // Array of rows
console.log(result.rowCount);    // Number of rows
console.log(result.fields);      // Column metadata

// Iterate over rows
for (const row of result.rows) {
  console.log(row.name, row.email);
}

Named Parameters

// Use named parameters for clarity
const result = await client.query({
  text: 'SELECT * FROM users WHERE email = $email AND status = $status',
  values: {
    email: 'user@example.com',
    status: 'active'
  }
});

Prepared Statements

// Create prepared statement
const prepared = await client.prepare(
  'get_user',
  'SELECT * FROM users WHERE id = $1'
);

// Execute multiple times
const result1 = await prepared.execute([123]);
const result2 = await prepared.execute([456]);

// Deallocate when done
await prepared.deallocate();

Transactions

// Manual transaction control
await client.query('BEGIN');
try {
  await client.query('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [100, 1]);
  await client.query('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [100, 2]);
  await client.query('COMMIT');
} catch (error) {
  await client.query('ROLLBACK');
  throw error;
}

// Helper method
await client.transaction(async (tx) => {
  await tx.query('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [100, 1]);
  await tx.query('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [100, 2]);
  // Auto-commits on success, rolls back on error
});

// Savepoints
await client.transaction(async (tx) => {
  await tx.query('INSERT INTO logs...');
  
  const savepoint = await tx.savepoint('sp1');
  try {
    await tx.query('INSERT INTO orders...');
  } catch (error) {
    await savepoint.rollback();
  }
  
  await savepoint.release();
});

JSON and Document Queries

// Insert JSON data
await client.query(
  'INSERT INTO users (name, profile) VALUES ($1, $2)',
  ['John Doe', { age: 30, city: 'NYC', preferences: { theme: 'dark' } }]
);

// Query JSON fields
const result = await client.query(`
  SELECT 
    name,
    profile->>'city' as city,
    profile->'preferences'->>'theme' as theme
  FROM users
  WHERE profile @> $1
`, [{ city: 'NYC' }]);

// Update nested JSON
await client.query(`
  UPDATE users
  SET profile = jsonb_set(profile, '{preferences,theme}', $1)
  WHERE id = $2
`, ['"light"', 123]);

// Type-safe JSON handling
interface UserProfile {
  age: number;
  city: string;
  preferences: {
    theme: 'light' | 'dark';
    notifications: boolean;
  };
}

const result = await client.query<{ name: string; profile: UserProfile }>(
  'SELECT name, profile FROM users WHERE id = $1',
  [123]
);

const profile: UserProfile = result.rows[0].profile;

Vector Operations

// Insert vectors
await client.query(
  'INSERT INTO documents (title, embedding) VALUES ($1, $2)',
  ['My Document', [0.1, 0.2, 0.3, /* ... */]]
);

// Similarity search
const queryEmbedding = [0.15, 0.25, 0.35, /* ... */];
const results = await client.query(`
  SELECT 
    id,
    title,
    1 - (embedding <=> $1::vector) as similarity
  FROM documents
  ORDER BY embedding <=> $1::vector
  LIMIT 10
`, [queryEmbedding]);

// Hybrid search (vector + text)
const results = await client.query(`
  SELECT 
    id,
    title,
    (0.7 * (1 - (embedding <=> $1::vector))) + 
    (0.3 * ts_rank(search_vector, to_tsquery($2))) as score
  FROM documents
  WHERE search_vector @@ to_tsquery($2)
  ORDER BY score DESC
  LIMIT 20
`, [queryEmbedding, 'database & performance']);

Streaming Large Result Sets

// Stream results for memory efficiency
const stream = client.stream('SELECT * FROM large_table');

stream.on('data', (row) => {
  console.log(row);
  // Process one row at a time
});

stream.on('end', () => {
  console.log('Finished streaming');
});

stream.on('error', (error) => {
  console.error('Stream error:', error);
});

// Async iterator support
for await (const row of client.stream('SELECT * FROM large_table')) {
  console.log(row);
  // Process rows as they arrive
}

TypeScript Support

// Define your schema types
interface User {
  id: number;
  name: string;
  email: string;
  created_at: Date;
  profile: {
    age: number;
    city: string;
  };
}

// Type-safe queries
const result = await client.query<User>(
  'SELECT * FROM users WHERE id = $1',
  [123]
);

// TypeScript knows the shape
const user: User = result.rows[0];
console.log(user.name);  // ✓ Type-safe
console.log(user.profile.city);  // ✓ Type-safe

// Query builder with types
import { QueryBuilder } from '@entropydb/client';

const qb = new QueryBuilder<User>('users');
const query = qb
  .select(['id', 'name', 'email'])
  .where('active', '=', true)
  .orderBy('created_at', 'DESC')
  .limit(10)
  .build();

const result = await client.query<Pick<User, 'id' | 'name' | 'email'>>(
  query.text,
  query.values
);

Error Handling

import { 
  DatabaseError, 
  ConnectionError,
  QueryError 
} from '@entropydb/client';

try {
  await client.query('SELECT * FROM users WHERE id = $1', [123]);
} catch (error) {
  if (error instanceof ConnectionError) {
    console.error('Connection failed:', error.message);
    // Retry logic
  } else if (error instanceof QueryError) {
    console.error('Query failed:', error.message);
    console.error('Query:', error.query);
    console.error('SQL State:', error.code);
  } else if (error instanceof DatabaseError) {
    console.error('Database error:', error.message);
  }
}

// Specific error codes
try {
  await client.query('INSERT INTO users (email) VALUES ($1)', ['duplicate@example.com']);
} catch (error) {
  if (error.code === '23505') {  // Unique violation
    console.error('Email already exists');
  }
}

Best Practices

1. Use Connection Pooling

Always use a connection pool in production for better performance and resource management.

2. Parameterize All Queries

Never use string concatenation for query values. Always use $1, $2, etc.

3. Handle Errors Gracefully

Always wrap queries in try-catch blocks and handle specific error types.

4. Close Connections

Release pooled connections after use and close pools on shutdown.

5. Use TypeScript

Leverage TypeScript for type safety and better IDE support.

Next Steps